1. 程式人生 > >R.Swift高效引用資原始檔

R.Swift高效引用資原始檔

  • 在iOS中當我們引用一張圖片的時候, 我們需要建立一個UIImage物件去引用圖片
  • 當我們需要解析json或者plist檔案的時候, 通常以Bundle.main的方式去解析
let oldImage = UIImage(named: "yellow-image")
let jsonData = Bundle.main.path(forResource: "menuList", ofType: "json")
  • 這裡得到的oldImagejsonData都是Optional型別, 那麼這樣就會有一個問題: 如果以上檔名字修改了或者輸錯了, 那麼得到的結果就是空的, 後期處理的時候就可能會程式崩潰或者沒有資料
  • 而且類似方法接受的都是字串物件, 所以即使傳入的錯誤的字串, 編譯程式的時候也不會報錯
  • 為了完美的解決上面的問題, 這裡介紹一個搞笑引用資原始檔的框架R.Swift

什麼是R.Swift

  • R.Swift是一款基於Swift平臺,針對iOS以及tvOS開發的資源引用框架
  • 它所針對的問題,就是類似於上面提到的一樣,避免使用字串來構造某些資源例項
  • R.Swift能夠使用類似語法R.資源型別.資源名稱來對某資源進行引用構建
  • R.Swift有著動態生成程式碼的機制, 它具有以下優點:
    • 程式碼自動補全:就像輸入其他的程式碼一樣,R.Swift支援IDE的程式碼自動補全
    • 自動檢測: 可以自動檢測程式碼是否存在問題, 當我們的資原始檔名修改的時候, 這是就會提示資源引用錯誤

安裝

  • 使用CocoaPods來對R.Swift進行安裝
  • 在你的Podfile檔案中新增如下程式碼, 並在終端執行pod install
pod 'R.swift'

配置資訊

  1. 如下圖所示, 新增一個New Run Script Phase

image

  1. Run Script拖動到Check Pods Manifest.lock的下面, 並新增指令碼
"$PODS_ROOT/R.swift/rswift" generate "$SRCROOT"

image

  1. Command+B編譯專案,在專案程式碼目錄下,會生成一個R.generated.swift 的檔案,將它拖如專案中

注意:

  • 不要勾選Copy items if needed選項,因為每次編譯都會生成新的R.generated.swift檔案,copy的話,舊的R.generated.swift將不會被覆蓋
  • 每當我們修改了資源,我們需要Command + B來編譯一下專案從而讓R.Swift自動進行配置更新
  • 這裡是坐著錄得一個如何匯入和配置R.Swift視訊教程

    1. 配置到此完成,這裡我們可以看一下R.generated.swift檔案的基本內容, 下面我們可以構建自己的專案了
struct R: Rswift.Validatable {
  fileprivate static let applicationLocale = hostingBundle.preferredLocalizations.first.flatMap(Locale.init) ?? Locale.current
  fileprivate static let hostingBundle = Bundle(for: R.Class.self)

  static func validate() throws {
    try intern.validate()
  }


  /// This `R.file` struct is generated, and contains static references to 1 files.
  struct file {
    /// Resource file `menuList.json`.
    static let menuListJson = Rswift.FileResource(bundle: R.hostingBundle, name: "menuList", pathExtension: "json")

    /// `bundle.url(forResource: "menuList", withExtension: "json")`
    static func menuListJson(_: Void = ()) -> Foundation.URL? {
      let fileResource = R.file.menuListJson
      return fileResource.bundle.url(forResource: fileResource)
    }

    fileprivate init() {}
  }


  /// This `R.image` struct is generated, and contains static references to 3 images.
  struct image {
    /// Image `blueimage`.
    static let blueimage = Rswift.ImageResource(bundle: R.hostingBundle, name: "blueimage")
    /// Image `computers`.
    static let computers = Rswift.ImageResource(bundle: R.hostingBundle, name: "computers")
    /// Image `yellow-image`.
    static let yellowImage = Rswift.ImageResource(bundle: R.hostingBundle, name: "yellow-image")

    /// `UIImage(named: "blueimage", bundle: ..., traitCollection: ...)`
    static func blueimage(compatibleWith traitCollection: UIKit.UITraitCollection? = nil) -> UIKit.UIImage? {
      return UIKit.UIImage(resource: R.image.blueimage, compatibleWith: traitCollection)
    }

    /// `UIImage(named: "computers", bundle: ..., traitCollection: ...)`
    static func computers(compatibleWith traitCollection: UIKit.UITraitCollection? = nil) -> UIKit.UIImage? {
      return UIKit.UIImage(resource: R.image.computers, compatibleWith: traitCollection)
    }

    /// `UIImage(named: "yellow-image", bundle: ..., traitCollection: ...)`
    static func yellowImage(compatibleWith traitCollection: UIKit.UITraitCollection? = nil) -> UIKit.UIImage? {
      return UIKit.UIImage(resource: R.image.yellowImage, compatibleWith: traitCollection)
    }

    fileprivate init() {}
  }

  /// This `R.nib` struct is generated, and contains static references to 2 nibs.
  struct nib {
    /// Nib `ImageFontController`.
    static let imageFontController = _R.nib._ImageFontController()
    /// Nib `NibTableViewCell`.
    static let nibTableViewCell = _R.nib._NibTableViewCell()

    /// `UINib(name: "ImageFontController", in: bundle)`
    static func imageFontController(_: Void = ()) -> UIKit.UINib {
      return UIKit.UINib(resource: R.nib.imageFontController)
    }

    /// `UINib(name: "NibTableViewCell", in: bundle)`
    static func nibTableViewCell(_: Void = ()) -> UIKit.UINib {
      return UIKit.UINib(resource: R.nib.nibTableViewCell)
    }

    fileprivate init() {}
  }


  /// This `R.storyboard` struct is generated, and contains static references to 3 storyboards.
  struct storyboard {
    /// Storyboard `LaunchScreen`.
    static let launchScreen = _R.storyboard.launchScreen()
    /// Storyboard `Main`.
    static let main = _R.storyboard.main()
    /// Storyboard `NibHome`.
    static let nibHome = _R.storyboard.nibHome()

    /// `UIStoryboard(name: "LaunchScreen", bundle: ...)`
    static func launchScreen(_: Void = ()) -> UIKit.UIStoryboard {
      return UIKit.UIStoryboard(resource: R.storyboard.launchScreen)
    }

    /// `UIStoryboard(name: "Main", bundle: ...)`
    static func main(_: Void = ()) -> UIKit.UIStoryboard {
      return UIKit.UIStoryboard(resource: R.storyboard.main)
    }

    /// `UIStoryboard(name: "NibHome", bundle: ...)`
    static func nibHome(_: Void = ()) -> UIKit.UIStoryboard {
      return UIKit.UIStoryboard(resource: R.storyboard.nibHome)
    }

    fileprivate init() {}
  }

   fileprivate init() {}
}

R.Swift的使用

Images - 圖片

//傳統方式
let oldImage = UIImage(named: "yellow-image")
oldImageView.image = oldImage

//R.Swift方式
let newImage = R.image.yellowImage()
newImageView.image = newImage

Custom fonts - 字型

這裡需要注意的一點是, 字型的引用需要引入一個ttf格式的字型檔案, 不然無法編譯除類似acmeLight的函式

//傳統方式
let lightFontTitle = UIFont(name: "Acme-Light", size: 22)

//R.Swift方式
let lightFontTitle = R.font.acmeLight(size: 22)

Resource files - 資料檔案

//傳統方式
let jsonData = Bundle.main.path(forResource: "menuList", ofType: "json")
let jsonUrl1 = Bundle.main.url(forResource: "menuList", withExtension: "json")

//R.Swift方式
let jsonData2 = R.file.menuListJson.path()
let newUrl = R.file.menuListJson()

Storyboards

//傳統方式
let nibVC1 = UIStoryboard(name: "NibHome", bundle: nil).instantiateInitialViewController() ?? UIViewController()

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialTabBarController = storyboard.instantiateInitialViewController() as? UITabBarController
let settingsController = storyboard.instantiateViewController(withIdentifier: "settingsController") as? SettingsControllerSettingsController


//R.Swift方式
let nibVC = R.storyboard.nibHome().instantiateInitialViewController() ?? UIViewController()

let storyboard = R.storyboard.main()
let initialTabBarController = R.storyboard.main.initialViewController()
let settingsController = R.storyboard.main.settingsController()

Nibs

//傳統方式
let nameOfNib = "CustomView"
let customViewNib = UINib(nibName: "CustomView", bundle: nil)
let rootViews = customViewNib.instantiate(withOwner: nil, options: nil)
let customView = rootViews[0] as? CustomView

let viewControllerWithNib = CustomViewController(nibName: "CustomView", bundle: nil)


//R.Swift方式
let nameOfNib = R.nib.customView.name
let customViewNib = R.nib.customView()
let rootViews = R.nib.customView.instantiate(withOwner: nil)
let customView = R.nib.customView.firstView(owner: nil)

let viewControllerWithNib = CustomViewController(nib: R.nib.customView)

Reusable table view cells - cell複用

這裡是UITableViewCell的註冊和使用為例, UICollectionViewCell亦同理

//傳統方式
let cellNib = UINib(nibName: "NibTableViewCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "NibTableViewCell")

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "NibTableViewCell", for: indexPath)
    cell?.leftLabel.text = dataArr[indexPath.row]
    return cell ?? UITableViewCell()
}


//R.Swift方式
tableView.register(R.nib.nibTableViewCell(), forCellReuseIdentifier: R.nib.nibTableViewCell.name)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: R.nib.nibTableViewCell.name, for: indexPath) as? NibTableViewCell
    cell?.leftLabel.text = dataArr[indexPath.row]
    return cell ?? UITableViewCell()
}

更多關於R.Swift的使用可參考官方文件Examples.md