iOS13簡單適配

yuyangkk發表於2019-07-30

iOS13的beta5版本已經出來了,APP適配也應該提上日程了,本文記錄下自己在適配時做的一些工作。

Q:我不想讓使用者使用DarkMode,可不可以?

A:當然可以,請往下看

在iOS13,為UIViewControllerUIView擴充套件了一個新的API-overrideUserInterfaceStyle,使用方法,官方文件大致是這麼說的:

通過設定overrideUserInterfaceStyle屬性以使該檢視及其子檢視具有特定的UIUserInterfaceStyle。但如果想要獲取當前的UIUserInterfaceStyle,需要改用traitCollection.userInterfaceStyle

儘可能使用UIViewController上的overrideUserInterfaceStyle屬性。僅在以下時間使用此屬性:  >- 在單個檢視或小檢視層次結構上區域性使用特定樣式。

  • 您希望在整個UIWindow及其檢視控制器和模態彈出的ViewController上使用特定樣式,且不希望強制更改整個應用程式具有樣式。 (如果您確實希望整個應用程式具有某種樣式,請不要使用它,而是在Info.plist中設定UIUserInterfaceStyle鍵。)
  1. 當設定在普通的UIView上時:
  • 此屬性僅影響此檢視及其子檢視的特徵。
  • 它不會影響任何檢視控制器或其他檢視控制器的子檢視。
  1. UIWindow上設定時:
  • 此屬性會影響rootViewController,從而影響整個檢視控制器和檢視層次結構。   >- 它還會影響該window模態出來的介面。

由此可見,overrideUserInterfaceStyle不僅會影響自己,還會影響自己的子檢視,換做window就會影響整個window中的所有檢視及檢視控制器,包括模態跳轉出來的檢視控制器。 而且,文件中也特別強調了,你可以設定整個應用程式只是用某種樣式,具體方法可以通過程式碼,也可以通過info.plist配置鍵User Interface Style,對應的Value為Light/Dark

// 此方法只會影響該window及其所有子檢視控制器和子檢視,不會影響別的window
if #available(iOS 13.0, *) {
    window?.overrideUserInterfaceStyle = .light;
}
複製程式碼

info.plist配置,會影響整個程式

info.plist配置,會影響整個程式

適配主要涉及的方面:

  • 模擬器除錯(simulator debug)
  • 圖片(assets)
  • 顏色(color)
  • 狀態列(status bar)
  • 模態跳轉(modal present)-2019.10.9更新
  • ...
模擬器除錯
  1. 執行專案
  2. 點選Xcode底部除錯欄中Environment Overrides
  3. 開啟Interface Style,就可以切換了。

效果圖adaptImage.gif

模擬器開啟darkmode.png

圖片適配

圖片適配,主要是我們本地圖片資源適配,網路圖片的話,還是比較繁瑣的,目前我們還沒做,只做下本地圖片適配。 圖片適配比較方便的就是通過Assets.xcassets進行圖片管理:

  1. 新增一個image set,重新命名如"adaptimage",選中該image set;
  2. 選中Attributes Inspector;
  3. 將Appearances由"None"改為"Any,Dark";
  4. 不同模式下設定不同圖片即可,mode 改變會自動選擇不同的圖片

Assets.png

當然圖片適配,你也可以直接使用判斷當前系統mode的方式進行區分,就我個人而言不是很喜歡這種方式,因為還需要監聽系統模式的變化,重寫UITraitEnvironment協議方法traitCollectionDidChange(_:),我們先看下協議方法:

/** Trait environments expose a trait collection that describes their environment. */
public protocol UITraitEnvironment : NSObjectProtocol {

    @available(iOS 8.0, *)
    var traitCollection: UITraitCollection { get }

    /** To be overridden as needed to provide custom behavior when the environment's traits change. */
    @available(iOS 8.0, *)
    func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
}

複製程式碼

所以,我們只需要在改變系統mode的時候,重寫代理:


func updateImageView() {
    let image = traitCollection.userInterfaceStyle == .light ? UIImage(named: "dark-ios") : UIImage(named: "white-ios")
    imageView.image = image
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    updateImageView()
}
複製程式碼
顏色適配

像圖片適配一樣,顏色適配有三種方式。 方法一:是通過Assets.xcassets新增一個Color Set,目前系統支援≥iOS11.0

extension UIColor {
    @available(iOS 11.0, *)
    public /*not inherited*/ init?(named name: String) // load from main bundle

    @available(iOS 11.0, *)
    public /*not inherited*/ init?(named name: String, in bundle: Bundle?, compatibleWith traitCollection: UITraitCollection?)
}
複製程式碼

colorset.png

方法二:程式碼建立動態顏色init(dynamicProvider: @escaping (UITraitCollection) -> UIColor),目前系統支援≥iOS 13.0

// 方法二
let titleColor = UIColor.init(dynamicProvider: { (trait) -> UIColor in
    return trait.userInterfaceStyle == .light ? UIColor.black : UIColor.white
})
btn.setTitleColor(titleColor, for: .normal)
複製程式碼

方法三:像圖片一樣,監聽模式轉變,重寫traitCollectionDidChange(_:)方法,不推薦

狀態列(StatusBar)

目前狀態列也增加了一種模式,由之前的兩種,變成了三種, 其中default由之前的黑色內容,變成了會根據系統模式,自動選擇當前展示lightContent還是darkContent

public enum UIStatusBarStyle : Int {
    case `default` // Automatically chooses light or dark content based on the user interface style

    @available(iOS 7.0, *)
    case lightContent // Light content, for use on dark backgrounds

    @available(iOS 13.0, *)
    case darkContent // Dark content, for use on light backgrounds
}
複製程式碼

我們在使用的時候,就可以重寫preferredStatusBarStyleget方法:

override var preferredStatusBarStyle: UIStatusBarStyle{
    get{
        return .lightContent
    }
}
複製程式碼
模態跳轉(modal present)

iOS13模態跳轉出來的介面,不再像之前版本是全屏的了,什麼意思呢?我們看下官方的註釋:

Defines the presentation style that will be used for this view controller when it is presented modally. Set this property on the view controller to be presented, not the presenter. If this property has been set to UIModalPresentationAutomatic, reading it will always return a concrete presentation style. By default UIViewController resolves UIModalPresentationAutomatic to UIModalPresentationPageSheet, but system-provided subclasses may resolve UIModalPresentationAutomatic to other concrete presentation styles. Participation in the resolution of UIModalPresentationAutomatic is reserved for system-provided view controllers. Defaults to UIModalPresentationAutomatic on iOS starting in iOS 13.0, and UIModalPresentationFullScreen on previous versions. Defaults to UIModalPresentationFullScreen on all other platforms.

大致意思是,如果將此屬性設定為UIModalPresentationAutomatic,則讀取該屬性將始終返回具體的呈現樣式。 預設情況下,UIViewControllerUIModalPresentationAutomatic解析為UIModalPresentationPageSheet,但是系統提供的子類可以將UIModalPresentationAutomatic解析為其他具體的呈現樣式。 保留UIModalPresentationAutomatic的解析度供系統提供的檢視控制器使用。從iOS 13.0開始,在iOS上預設為UIModalPresentationAutomatic,在以前的版本上預設為UIModalPresentationFullScreen。 在所有其他平臺上,預設為UIModalPresentationFullScreenUIModalPresentationPageSheet就是下面的樣子:

UIModalPresentationPageSheet

知道了原因,我們做適配也簡單了,就是設定下屬性的事:

let second = SecondViewController()
second.modalPresentationStyle = .fullScreen
present(second, animated: true, completion: nil)
複製程式碼

目前我們iOS的適配只做了這些,後續如果有其他需要適配的地方,再更新文章。

相關文章