Beispiel für eine schnelle Erweiterung


82

Ich wollte ursprünglich wissen, wie man so etwas macht

UIColor.myCustomGreen

damit ich meine eigenen Farben definieren und sie in meiner App verwenden kann.

Ich hatte zuvor Erweiterungen studiert und dachte, ich könnte sie wahrscheinlich verwenden, um mein Problem zu lösen, aber ich konnte mich nicht genau erinnern, wie ich Erweiterungen einrichten sollte. Die Suche bei Google zum Zeitpunkt dieses Schreibens nach "Swift-Erweiterung" führte zu einer Dokumentation , mehreren langen Tutorials und einer eher wenig hilfreichen Frage zum Stapelüberlauf .

Die Antworten sind also da draußen, aber es erfordert einige Zeit, die Dokumente und Tutorials durchzuarbeiten. Ich habe mich entschlossen, diese Frage und die folgende Antwort zu schreiben, um dem Stapelüberlauf einige bessere Suchschlüsselwörter hinzuzufügen und eine schnelle Auffrischung der Einrichtung von Erweiterungen bereitzustellen.

Insbesondere wollte ich wissen:

  • Wo befinden sich die Erweiterungen (Datei- und Namenskonvention)?
  • Was ist die Erweiterungssyntax?
  • Was sind einige einfache Beispiele für die allgemeine Verwendung?

Antworten:


172

Eine Erweiterung erstellen

Fügen Sie eine neue Swift-Datei mit Datei> Neu> Datei ...> iOS> Quelle> Swift-Datei hinzu . Sie können es so nennen, wie Sie wollen.

Die allgemeine Namenskonvention lautet TypeName + NewFunctionality.swift .

Geben Sie hier die Bildbeschreibung ein

Beispiel 1 - Double

Double + Conversions.swift

import Swift // or Foundation

extension Double {

    func celsiusToFahrenheit() -> Double {
        return self * 9 / 5 + 32
    }

    func fahrenheitToCelsius() -> Double {
        return (self - 32) * 5 / 9
    }
}

Verwendung:

let boilingPointCelsius = 100.0
let boilingPointFarenheit = boilingPointCelsius.celsiusToFahrenheit()
print(boilingPointFarenheit) // 212.0

Beispiel 2 - String

String + Shortcuts.swift

import Swift // or Foundation

extension String {

    func replace(target: String, withString: String) -> String {
        return self.replacingOccurrences(of: target, with: withString)
    }
}

Verwendung:

let newString = "the old bike".replace(target: "old", withString: "new")
print(newString) // "the new bike"

Hier sind einige häufigere StringErweiterungen.

Beispiel 3 - UIColor

UIColor + CustomColor.swift

import UIKit

extension UIColor {

    class var customGreen: UIColor {
        let darkGreen = 0x008110
        return UIColor.rgb(fromHex: darkGreen)
    }

    class func rgb(fromHex: Int) -> UIColor {

        let red =   CGFloat((fromHex & 0xFF0000) >> 16) / 0xFF
        let green = CGFloat((fromHex & 0x00FF00) >> 8) / 0xFF
        let blue =  CGFloat(fromHex & 0x0000FF) / 0xFF
        let alpha = CGFloat(1.0)

        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
}

Siehe auch hier .

Verwendung:

view.backgroundColor = UIColor.customGreen

Geben Sie hier die Bildbeschreibung ein

Anmerkungen

  • Sobald Sie eine Erweiterung definiert haben, kann sie genau wie die integrierten Klassenfunktionen überall in Ihrer App verwendet werden.
  • Wenn Sie nicht genau wissen, wie die Funktions- oder Eigenschaftssyntax aussehen soll, können OptionSie auf eine ähnliche integrierte Methode klicken. Wenn ich zum Beispiel Option+ + anklicke, UIColor.greenColorsehe ich, dass die Deklaration lautet class func greenColor() -> UIColor. Das gibt mir einen guten Hinweis, wie ich meine benutzerdefinierte Methode einrichten kann.
  • Apple-Dokumentation für Erweiterungen
  • In Objective-C werden Erweiterungen als Kategorien bezeichnet.

2
Warum verwendet UIColor classdie Funktion, aber String nicht?
JZAU

5
@jacky, das Schlüsselwort 'class' vor der Funktion macht es zu einer statischen Typmethode im Gegensatz zu einer Instanzmethode. Auf diese Weise müssen Sie UIColor nicht instanziieren, um die benutzerdefinierte Farbe zu erhalten. Siehe diese Antwort für weitere Details: stackoverflow.com/a/31630431/3681880
Suragch

Das wird mich heute abdecken, aber wie macht man einzigartige Erweiterungen, dh Klassentier, Erweiterungskuh, Erweiterungskatze, Erweiterungshund?
Lorne K

2
@ LorneK, es hört sich für mich so an, als würden Sie über Unterklassen sprechen . Eine Erweiterung fügt einem vorhandenen Klassentyp lediglich zusätzliche Funktionen oder Methoden hinzu. Siehe diesen Artikel auch für einen Vergleich.
Suragch

Es ist erwähnenswert, dass xcode Erweiterungsmethoden möglicherweise nicht sofort aufnimmt und Ihre Anrufe als ungelöst betrachtet. Das Auslösen eines Builds macht das automatisch! Tricky.
Akash Agarwal

9

Probieren Sie einige neue Erweiterungsmethoden aus:

UIColor

extension UIColor{
 //get new color from rgb value
  class func RGB(_ red:CGFloat , andGreenColor green:CGFloat, andBlueColor blue:CGFloat, withAlpha alpha:CGFloat) -> UIColor
  {
    let color = UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
    return color
  }
}

 //return color from comma separated string of RGB paramater
  convenience init(rgbString :String, alpha:CGFloat = 1.0){
    let arrColor = rgbString.components(separatedBy: ",")
    let red:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[0])!)
    let green:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[1])!)
    let blue:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[2])!)
    self.init(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
  }

  //return color from hexadecimal value
  //let color2 = UIColor(rgbHexaValue: 0xFFFFFFFF)
  convenience init(rgbHexaValue: Int, alpha: CGFloat = 1.0) {
    self.init(red:  CGFloat((rgbHexaValue >> 16) & 0xFF), green: CGFloat((rgbHexaValue >> 8) & 0xFF), blue: CGFloat(rgbHexaValue & 0xFF), alpha: alpha)
  }
}

UITextField

extension UITextField{

//set cornerRadius
  func cornerRadius(){
    self.layoutIfNeeded()
    self.layer.cornerRadius = self.frame.height / 2
    self.clipsToBounds = true
  }

  //set bordercolor
  func borderColor(){
      self.layer.borderColor = TEXTFIELD_BORDER_COLOR.cgColor
      self.layer.borderWidth = 1.0
  }

  //set borderWidth
  func borderWidth(size:CGFloat){
    self.layer.borderWidth = size
  }

  //check textfield is blank
  func blank() -> Bool{
    let strTrimmed = self.text!.trim()//get trimmed string
    if(strTrimmed.characters.count == 0)//check textfield is nil or not ,if nil then return false
    {
      return true
    }
    return false
  }

  //set begginning space - left space
  func setLeftPadding(paddingValue:CGFloat) {
    let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: paddingValue, height: self.frame.size.height))
    self.leftViewMode = .always
    self.leftView = paddingView
  }

  //set end of space
  func setRightPadding(paddingValue:CGFloat){
    let paddingView = UIView(frame: CGRect(x: (self.frame.size.width - paddingValue), y: 0, width: paddingValue, height: self.frame.size.height))
    self.rightViewMode = .always
    self.rightView = paddingView
  }
}

UIFont

extension UIFont{
 // Returns a scaled version of UIFont
  func scaled(scaleFactor: CGFloat) -> UIFont {
    let newDescriptor = fontDescriptor.withSize(fontDescriptor.pointSize * scaleFactor)
    return UIFont(descriptor: newDescriptor, size: 0)
  }
}

UIImage

public enum ImageFormat {
  case PNG
  case JPEG(CGFloat)
}


extension UIImage {
  //convert image to base64 string
  func toBase64() -> String {
    var imageData: NSData
    switch format {
    case .PNG: imageData = UIImagePNGRepresentation(self)! as NSData
    case .JPEG(let compression): imageData = UIImageJPEGRepresentation(self, compression)! as NSData
    }
    return imageData.base64EncodedString(options: .lineLength64Characters)
  }

  //convert string to image
  class func base64ToImage(toImage strEncodeData: String) -> UIImage {
    let dataDecoded  = NSData(base64Encoded: strEncodeData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
    let image = UIImage(data: dataDecoded as Data)
    return image!
  }

  //Function for store file/Image into local directory. If image is already on the directory then first remove it and replace new image/File on that location
  func storedFileIntoLocal(strImageName:String) -> String{
    var strPath = ""
    let documentDirectory1 = NSString.init(string: String.documentDirectory())
    let imageName:String = strImageName + ".png"
    let imagePath = documentDirectory1.appendingPathComponent(imageName)
    strPath = imagePath
    let fileManager = FileManager.default
    let isExist = fileManager.fileExists(atPath: String.init(imagePath))
    if(isExist == true)
    {
      do {
        try fileManager.removeItem(atPath: imagePath as String)//removing file if exist
        // print("Remove success")
      } catch {
        print(error)
      }
    }
    let imageData:Data = UIImageJPEGRepresentation(self, 0.5)!
    do {
      try imageData.write(to: URL(fileURLWithPath: imagePath as String), options: .atomic)
    } catch {
      print(error)
      strPath = "Failed to cache image data to disk"
      return strPath
    }

    return strPath
  }


  //function for resize image
  func resizeImage(targetSize: CGSize) -> UIImage {
    let size = self.size

    let widthRatio  = targetSize.width  / self.size.width
    let heightRatio = targetSize.height / self.size.height

    // Figure out what our orientation is, and use that to form the rectangle
    var newSize: CGSize
    if(widthRatio > heightRatio) {
      newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
    } else {
      //                        newSize = size
      newSize = CGSize(width: size.width * widthRatio,  height: size.height * widthRatio)
    }

    // This is the rect that we've calculated out and this is what is actually used below
    let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

    // Actually do the resizing to the rect using the ImageContext stuff
    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
    self.draw(in: rect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
  }
}

Datum

let YYYY_MM_DD_HH_MM_SS_zzzz = "yyyy-MM-dd HH:mm:ss +zzzz"
let YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"
let DD_MM_YYYY = "dd-MM-yyyy"
let MM_DD_YYYY = "MM-dd-yyyy"
let YYYY_DD_MM = "yyyy-dd-MM"
let YYYY_MM_DD_T_HH_MM_SS = "yyyy-MM-dd'T'HH:mm:ss"

extension Date{

  //convert string to date
  static func convertStringToDate(strDate:String, dateFormate strFormate:String) -> Date{
    let dateFormate = DateFormatter()
    dateFormate.dateFormat = strFormate
    dateFormate.timeZone = TimeZone.init(abbreviation: "UTC")
    let dateResult:Date = dateFormate.date(from: strDate)!

    return dateResult
  }

  //Function for old date format to new format from UTC to local
  static func convertDateUTCToLocal(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    dateFormatterUTC.timeZone = NSTimeZone(abbreviation: "UTC") as TimeZone!//set UTC timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate)  as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.local//set localtimeZone
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate:String = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Convert without UTC to local
  static func convertDateToLocal(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    //set local timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate) as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.local
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Convert Date to String
  func convertDateToString(strDateFormate:String) -> String{
      let dateFormatter = DateFormatter()
      dateFormatter.dateFormat = strDateFormate
      let strDate = dateFormatter.string(from: self)
//      dateFormatter = nil
      return strDate
  }


  //Convert local to utc
  static func convertLocalToUTC(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    dateFormatterUTC.timeZone = NSTimeZone.local as TimeZone!//set UTC timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate)  as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.init(abbreviation: "UTC")! as TimeZone//set localtimeZone
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate:String = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Comparison two date
  static func compare(date:Date, compareDate:Date) -> String{
    var strDateMessage:String = ""
    let result:ComparisonResult = date.compare(compareDate)
    switch result {
    case .orderedAscending:
      strDateMessage = "Future Date"
      break
    case .orderedDescending:
      strDateMessage = "Past Date"
      break
    case .orderedSame:
      strDateMessage = "Same Date"
      break
    default:
      strDateMessage = "Error Date"
      break
    }
    return strDateMessage
  }
}

Aufruf dieser Funktionen:

let color1 = UIColor.RGB(100.0, andGreenColor: 200.0, andBlueColor: 300.0, withAlpha: 1.0)
let color2 = UIColor.init(rgbHexaValue: 800000, alpha: 1.0)
let color3 = UIColor.init(rgbString: ("100.0,200.0,300.0", alpha: 1.0)

self.txtOutlet.cornerRadius()
self.txtOutlet.borderColor()
self.txtOutlet.setLeftPadding(paddingValue: 20.0)
self.txtOutlet.setRightPadding(paddingValue: 20.0)

let yourScaledFont = self.dependentView.font.scaled(scaleFactor: n as! CGFloat)
let base64String = (image?.toBase64(format: ImageFormat.PNG))!
let resultImage = UIImage.base64ToImage(toImage: base64String)
let path = yourImage.storedFileIntoLocal(strImageName: "imagename")

6

Swift 3.0 Beispiel:

extension UITextField 
{    

    func useUnderline() {
        let border = CALayer()
        let borderWidth = CGFloat(1.0)
        border.borderColor = UIColor.lightGray.cgColor
        border.frame = CGRect(origin: CGPoint(x: 0,y :self.frame.size.height - borderWidth), size: CGSize(width: self.frame.size.width, height: self.frame.size.height))
        border.borderWidth = borderWidth
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true
    }
}

In Ihrem Fall würde ich lieber eine neue Klasse erstellen, die von UITextField geerbt wurde, anstatt das ursprüngliche UITextField zu erweitern. Es gibt mehr Flexibilität. Was ist, wenn ich in derselben App unterschiedliche Stile für meine Textfelder verwenden möchte? Erweiterungen werden der ursprünglichen Klasse global hinzugefügt.
Michal Cichon

4

Unterstreichen Sie den Text in UITextField

Wird in der Funktion verwendet ViewDidLoad()

firstNametext.underlined(0.5)

Erweiterung

extension UITextField {

    func underlined(_ size:Double){
        let border = CALayer()
        let width = CGFloat(size)
        border.borderColor = UIColor.red.cgColor
        border.frame = CGRect(x: 0, y: self.frame.size.height - width, 
        width:  self.frame.size.width, height: self.frame.size.height)
        border.borderWidth = width
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true }
    }
}

Hallo! Willkommen bei stackoverflow! Gute Antworten zum Stapelüberlauf haben normalerweise eine Erklärung. Nur etwas, woran Sie denken sollten, wenn Sie das nächste Mal eine Frage beantworten!
Qwerty

@ Qwerty, es hatte eine Erklärung, aber es wurde wie Code formatiert. Ich habe es neu formatiert.
Suragch

3

UIColor + util.swift

import UIKit


extension UIColor{


    class func getCustomBlueColor() -> UIColor
    {
        return UIColor(red:0.043, green:0.576 ,blue:0.588 , alpha:1.00)
    }

    func getNameofColour() ->String
    {
        return "myOrange"
    }

}

Verwendung :

NSLog("\(UIColor.getCustomBlueColor())")
let color=UIColor(red:0.043, green:0.576 ,blue:0.588 , alpha:1.00);
NSLog(color.getNameofColour())

Ich hoffe du siehst das was den Unterschied ausmacht. Einer der Funktion beginnend mit Klasse func ein anderes nur Start Func . Sie können verwenden, was Sie mögen.


Es gibt einen Fehler, wenn ich Uikit importiere. Mache ich etwas falsch?
Nabeel Khan

0

Eines der besten Beispiele für Erweiterungs- und Komfortinitialisierer:

 extension UIActivityIndicatorView {
    convenience init(activityIndicatorStyle: UIActivityIndicatorViewStyle, color: UIColor, placeInTheCenterOf parentView: UIView) {
    self.init(activityIndicatorStyle: activityIndicatorStyle)
    center = parentView.center
    self.color = color
    parentView.addSubview(self)
  }
}

Sie können es auf folgende Arten verwenden:

  1. Initialisieren Sie activityIndicator

    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge, color: .gray,  placeInTheCenterOf: view)
    
  2. Starten Sie die Animation der Aktivitätsanzeige

    activityIndicator.startAnimating()
    
  3. Beenden Sie die Animation der Aktivitätsanzeige

    activityIndicator.stopAnimating()
    

0

Wenn Sie eine Farbe mit einem bestimmten Farbton verwenden möchten, wie er in Markenhandbüchern verwendet wird: Swift 4.2 + xcode 9.4.1.

extension UIColor {
    func withTint(tint: CGFloat)->UIColor {

        var tint = max(tint, 0)
        tint = min(tint, 1)
        /* Collect values of sender */
        var r : CGFloat = 0
        var g : CGFloat = 0
        var b : CGFloat = 0
        var a : CGFloat = 0
        self.getRed(&r, green: &g, blue: &b, alpha: &a)

        /* Calculate the tint */
        r = r+(1-r)*(1-tint)
        g = g+(1-g)*(1-tint)
        b = b+(1-b)*(1-tint)
        a = 1

        return UIColor.init(red: r, green: g, blue: b, alpha: a)
    }
}

In deinem Code

let redWithTint = UIColor.red.withTint(tint: 0.4)

0

Hier ist ein Erweiterungsbeispiel eines auffälligen Animationseffekts, der mit Zellen aus UITableView funktioniert. Jede Zelle wächst von einer Punktquelle auf eine normale Größe, wenn Sie durch eine UITableView scrollen. Passen Sie das Animations-Timing wie gewünscht an.

Da jede Zelle beim Scrollen mit einer kleinen Zeitverschiebung angezeigt wird, ist der Effekt gut! Sehen Sie sich diesen 15-Sekunden-Clip an, der den Effekt zeigt: https://www.youtube.com/watch?v=BVeQpno56wU&feature=youtu.be


extension UITableViewCell {

    func growCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)

        UIView.animate(withDuration: TimeInterval(0.35), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.transform = CGAffineTransform(scaleX: 1, y: 1)

        }, completion: nil)

    }
}

Um die Erweiterung zu verwenden, rufen Sie sie kurz vor der Rückgabe der Zelle in cellForRowAt auf, wie unten gezeigt:


            cell.growCellDuringPresentation(thisCell: cell)
            return cell

Beachten Sie, dass dieselbe Methode funktioniert, wenn Zellen für eine Sammlungsansicht zurückgegeben werden.

Hier ist eine Erweiterung, die genauso funktioniert, außer dass sie die Zellen während der Präsentation dreht:


extension UITableViewCell {

    func rotateCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.transform = CGAffineTransform(rotationAngle: .pi)

        UIView.animate(withDuration: TimeInterval(0.35), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.transform = CGAffineTransform(rotationAngle: 0)

        }, completion: nil)

    }
}

Es heißt ähnlich:


            cell.rotateCellDuringPresentation(thisCell: cell)
            return cell

Hier ist eine Erweiterung entlang derselben Linie, die die Zellen in X-Richtung übersetzt


extension UITableViewCell {

    func translateCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.layer.transform = CATransform3DMakeTranslation(-300, 0, 0)

        UIView.animate(withDuration: TimeInterval(0.5), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.layer.transform = CATransform3DMakeTranslation(0, 0, 0)

        }, completion: nil)

    }
}

Es heißt ähnlich:


            cell.translateCellDuringPresentation(thisCell: cell)
            return cell
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.