Приклад швидкого розширення


82

Спочатку я хотів знати, як зробити щось подібне

UIColor.myCustomGreen

щоб я міг визначати власні кольори та використовувати їх у своєму додатку.

Я вивчав розширення раніше, і я думав, що, напевно, можу використати їх для вирішення своєї проблеми, але я не міг пригадати, як саме налаштовувати розширення. Пошук у Google на момент написання статті для «швидкого розширення» призвів до документації , кількох довгих навчальних посібників та досить безглуздого запитання щодо переповнення стека .

Отже, відповіді є, але для цього потрібно трохи перекопати документи та навчальні посібники. Я вирішив написати це питання та наступну відповідь, щоб додати кілька кращих ключових слів для пошуку до Stack Overflow та забезпечити швидке оновлення способу налаштування розширень.

Зокрема, я хотів би знати:

  • Де знаходяться розширення (конвенція щодо файлів та імен)?
  • Що таке синтаксис розширення?
  • Які кілька простих загальноприйнятих прикладів?

Відповіді:


173

Створення розширення

Додайте новий швидкий файл за допомогою Файл> Новий> Файл ...> iOS> Джерело> Файл швидкого доступу . Ви можете назвати це як завгодно.

Загальна конвенція про іменування полягає в тому, щоб називати її TypeName + NewFunctionality.swift .

введіть тут опис зображення

Приклад 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
    }
}

Використання:

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

Приклад 2 - String

Рядок + ярлики

import Swift // or Foundation

extension String {

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

Використання:

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

Ось ще кілька поширених Stringрозширень.

Приклад 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)
    }
}

Дивіться також тут .

Використання:

view.backgroundColor = UIColor.customGreen

введіть тут опис зображення

Примітки

  • Як тільки ви визначите розширення, його можна використовувати де завгодно у вашій програмі, як і вбудовані функції класу.
  • Якщо ви не впевнені, як саме повинен виглядати синтаксис функції або властивості, ви можете Option+ натиснути подібний вбудований метод. Наприклад, коли я Option+ натиснув, UIColor.greenColorя бачу, що декларація є class func greenColor() -> UIColor. Це дає мені хороший підказка про те, як налаштувати власний метод.
  • Документація Apple для розширень
  • У Objective-C розширення відомі як категорії.

2
чому UIColor використовується classдля визначення функції, а String - ні?
JZAU

5
@jacky, ключове слово 'class' перед функцією робить його статичним методом типу на відміну від методу екземпляра. Таким чином, вам не потрібно створювати копію UIColor, щоб отримати власний колір. Дивіться цей відповідь для більш докладної інформації: stackoverflow.com/a/31630431/3681880
Suragch

це охопить мене сьогодні, але як ви робите унікальні розтяжки, тобто клас тварини, корова розширення, кішка розширення, собака розширення?
Lorne K

2
@LorneK, Мені здається, що Ви говорите про підкласифікацію . Розширення просто додає додаткову функціональність або методи до існуючого типу класу. Див. Цю статтю також для порівняння.
Сурагч

Варто зазначити, що xcode може не миттєво приймати розширення та розглядати ваші дзвінки як невирішені. Запуск збірки автоматично робить це! Хитро.
Akash Agarwal

9

Спробуйте ось кілька нових методів розширення:

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

Дата

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

Виклик цих функцій:

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:

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

У вашому випадку я волів би створити новий клас, успадкований від UITextField, замість того, щоб розширювати оригінальний UITextField. Це надає більшу гнучкість. Що робити, якщо я хочу використовувати різні стилі для своїх текстових полів в одному додатку? Розширення додаються до початкового класу в усьому світі.
Michal Cichon

4

Підкресли текст у UITextField

Використовується у функції ViewDidLoad()

firstNametext.underlined(0.5)

Розширення

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

Привіт! Ласкаво просимо до stackoverflow! Хороші відповіді щодо stackoverflow, як правило, мають якесь пояснення поряд. Просто щось подумайте, коли наступного разу відповідатимете на питання!
Qwerty

@Qwerty, він мав пояснення, але він був відформатований як код. Я переформатував його.
Сурагч

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

}

Використання :

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

Сподіваюся, ви бачите, що різниця полягає в цьому. Один із функцій, що починається з класу func, інший починається лише з func . ви можете використовувати те, що вам подобається.


при імпорті uikit видається помилка, я щось роблю не так?
Nabeel Khan

0

Один з найкращих прикладів розширення та зручності ініціалізатора:

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

Ви можете використовувати його наступними способами:

  1. Ініціалізуйте ActivityIndicator

    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge, color: .gray,  placeInTheCenterOf: view)
    
  2. Почніть анімувати показник активності

    activityIndicator.startAnimating()
    
  3. Зупиніть анімацію показника активності

    activityIndicator.stopAnimating()
    

0

Якщо ви хочете використовувати колір із заданим відтінком, як це використовується в посібниках бренду: 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)
    }
}

У вашому коді

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

0

Ось приклад розширення ефектного анімаційного ефекту, який працює з клітинками з UITableView. Кожна клітинка зростає з точкового джерела до нормального розміру під час прокрутки UITableView. Налаштуйте час анімації за бажанням.

Оскільки кожна клітинка під час прокрутки відображається з невеликим коливанням часу, ефект гарно брижає! Дивіться цей 15-секундний кліп, який демонструє ефект: 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)

    }
}

Для використання розширення ви робите виклик до нього безпосередньо перед тим, як комірку повертають у cellForRowAt, як показано нижче:


            cell.growCellDuringPresentation(thisCell: cell)
            return cell

Зауважте, цей самий метод працює при поверненні комірок для подання колекції.

Ось розширення, яке працює точно так само, за винятком того, що воно обертає клітинки під час презентації:


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)

    }
}

Це називається так:


            cell.rotateCellDuringPresentation(thisCell: cell)
            return cell

Ось розширення за тими самими лініями, яке переводить клітинки у напрямку X.


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)

    }
}

Це називається так:


            cell.translateCellDuringPresentation(thisCell: cell)
            return cell
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.