Swift extension example
Asked Answered
T

8

88

I was originally wanting to know how to make something like this

UIColor.myCustomGreen

so that I could define my own colors and use them throughout my app.

I had studied extensions before and I thought that I could probably use them to solve my problem, but I couldn't remember exactly how to set extensions up. Searching on Google at the time of this writing for "Swift extension" resulted in the documentation, several long tutorials, and a rather unhelpful Stack Overflow question.

So the answers are out there, but it takes some digging through the docs and tutorials. I decided to write this question and the following answer to add some better search keywords to Stack Overflow and to provide a quick refresher on how extensions are set up.

Specifically I wanted to know:

  • Where do the extensions reside (file and naming convention)?
  • What is the extension syntax?
  • What are a few simple common use examples?
Testes answered 26/11, 2015 at 15:45 Comment(0)
T
191

Creating an extension

Add a new swift file with File > New > File... > iOS > Source > Swift File. You can call it what you want.

The general naming convention is to call it TypeName+NewFunctionality.swift.

enter image description here

Example 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
    }
}

Usage:

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

Example 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)
    }
}

Usage:

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

Here are some more common String extensions.

Example 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)
    }
}

See here also.

Usage:

view.backgroundColor = UIColor.customGreen

enter image description here

Notes

  • Once you define an extension it can be used anywhere in your app just like the built in class functions.
  • If you are not sure of exactly what the function or property syntax should look like, you can Option+click a similar built in method. For example, when I Option+clicked UIColor.greenColor I see the declaration is class func greenColor() -> UIColor. That gives me a good clue for how to set up my custom method.
  • Apple Documentation for Extensions
  • In Objective-C extensions are known as categories.
Testes answered 26/11, 2015 at 15:45 Comment(6)
why UIColor use class to define function but String not?Leeth
@jacky, the 'class' keyword before the function makes it a static Type Method as opposed to an Instance Method. This way you don't have to instanciate UIColor in order to get the custom color. See this answer for more details: https://mcmap.net/q/23442/-missing-argument-for-parameter-1-in-call-error-for-function-with-no-params-swiftTestes
this will cover me today, but how do you do unique extensions ie class animal, extension cow, extension cat, extension dog?Breadfruit
@LorneK, It sounds to me like you are talking about subclassing. An extension just adds additional functionality or methods to an existing class type. See this article also for a comparison.Testes
Its worthy to note that xcode may not instantly pickup extension methods and consider your calls as unresolved. Triggering a build automatically does that! Tricky.Marentic
I'm currently following an apple guide for creating an App and it defines a struct in the top level. Then defines several extensions to it, in the same file. Is this just showing what extensions do or is there an advantage to this instead of just adding the extended code into the main struct?Lxx
P
9

Try this some new extension methods:

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!
  }
}

Date

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
  }
}

Calling this functions:

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")
Peri answered 25/8, 2017 at 12:42 Comment(0)
M
6

Swift 3.0 example:

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
    }
}
Macdougall answered 19/10, 2016 at 2:59 Comment(1)
In your case I would rather create a new class inherited from UITextField instead of extending the original UITextField. It gives more flexibility. What if I want to use different styles for my text fields in the same app? Extensions are added to the original class globally.Aeolian
O
4

Underline text in UITextField

Used in function ViewDidLoad()

firstNametext.underlined(0.5)

Extension

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 }
    }
}
Obe answered 23/4, 2018 at 17:11 Comment(2)
Hi! Welcome to stackoverflow! Good answers on stackoverflow, will usually have some sort of explanation alongside them. Just something to think of the next time you answer a question!Fauch
@Qwerty, it had explanation, but it was formatted like code. I reformatted it.Testes
P
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"
    }

}

Usage :

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

I hope you see that what is difference . One of Function starting with class func another one starting only func . you can use which you like.

Powered answered 10/1, 2016 at 11:7 Comment(1)
it gives an error when I import uikit, am i doing something wrong?Appling
P
0

One of the best example of extension and convenience initializer :

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

You can use it in following ways :

  1. Initialize activityIndicator

    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge, color: .gray,  placeInTheCenterOf: view)
    
  2. Start animating activity indicator

    activityIndicator.startAnimating()
    
  3. Stop animating activity indicator

    activityIndicator.stopAnimating()
    
Pronator answered 21/2, 2018 at 11:59 Comment(0)
C
0

If you like to use a colour with a given tint like used in brand manuals: 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 your code

let redWithTint = UIColor.red.withTint(tint: 0.4)
Countenance answered 15/8, 2018 at 17:17 Comment(0)
V
0

Here is an extension example of an eye catching animation effect that works with cells from UITableView. Each cell grows from a point source to normal size as you scroll a UITableView. Adjust the animation timing as desired.

Since each cell shows up with a little time stagger while scrolling, the effect ripples nicely! See this 15 second clip that showcases the effect : 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)

    }
}

To use the extension you make a call to it just before the cell is returned in cellForRowAt, like shown below :


            cell.growCellDuringPresentation(thisCell: cell)
            return cell

Note this same method works when returning cells for a collection view.

Here is an extension that works exactly the same, except that it rotates the cells during presentation :


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)

    }
}

It's called similarly :


            cell.rotateCellDuringPresentation(thisCell: cell)
            return cell

Here is an extension along the same lines that translates the cells in the X direction


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)

    }
}

It's called similarly :


            cell.translateCellDuringPresentation(thisCell: cell)
            return cell
Vincent answered 3/5, 2020 at 4:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.