中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

iOS13原生端適配的示例分析

發布時間:2021-08-26 14:15:39 來源:億速云 閱讀:148 作者:小新 欄目:移動開發

這篇文章主要介紹了iOS13原生端適配的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

1. KVC訪問私有屬性

這次iOS 13系統升級,影響范圍最廣的應屬KVC訪問修改私有屬性了,直接禁止開發者獲取或直接設置私有屬性。而KVC的初衷是允許開發者通過Key名直接訪問修改對象的屬性值,為其中最典型的 UITextField 的 _placeholderLabel、UISearchBar 的 _searchField。

造成影響:在iOS 13下App閃退

錯誤代碼:

// placeholderLabel私有屬性訪問
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];
// searchField私有屬性訪問
UISearchBar *searchBar = [[UISearchBar alloc] init];
UITextField *searchTextField = [searchBar valueForKey:@"_searchField"];

解決方案:

使用 NSMutableAttributedString 富文本來替代KVC訪問 UITextField 的 _placeholderLabel

textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];

因此,可以為UITextFeild創建Category,專門用于處理修改placeHolder屬性提供方法

#import "UITextField+ChangePlaceholder.h"

@implementation UITextField (Change)

- (void)setPlaceholderFont:(UIFont *)font {

 [self setPlaceholderColor:nil font:font];
}

- (void)setPlaceholderColor:(UIColor *)color {

 [self setPlaceholderColor:color font:nil];
}

- (void)setPlaceholderColor:(nullable UIColor *)color font:(nullable UIFont *)font {

 if ([self checkPlaceholderEmpty]) {
  return;
 }

 NSMutableAttributedString *placeholderAttriString = [[NSMutableAttributedString alloc] initWithString:self.placeholder];
 if (color) {
  [placeholderAttriString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, self.placeholder.length)];
 }
 if (font) {
  [placeholderAttriString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholder.length)];
 }

 [self setAttributedPlaceholder:placeholderAttriString];
}

- (BOOL)checkPlaceholderEmpty {
 return (self.placeholder == nil) || ([[self.placeholder stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0);
}

關于 UISearchBar,可遍歷其所有子視圖,找到指定的 UITextField 類型的子視圖,再根據上述 UITextField 的通過富文本方法修改屬性。

#import "UISearchBar+ChangePrivateTextFieldSubview.h"

@implementation UISearchBar (ChangePrivateTextFieldSubview)

/// 修改SearchBar系統自帶的TextField
- (void)changeSearchTextFieldWithCompletionBlock:(void(^)(UITextField *textField))completionBlock {

 if (!completionBlock) {
  return;
 }
 UITextField *textField = [self findTextFieldWithView:self];
 if (textField) {
  completionBlock(textField);
 }
}

/// 遞歸遍歷UISearchBar的子視圖,找到UITextField
- (UITextField *)findTextFieldWithView:(UIView *)view {

 for (UIView *subview in view.subviews) {
  if ([subview isKindOfClass:[UITextField class]]) {
   return (UITextField *)subview;
  }else if (subview.subviews.count > 0) {
   return [self findTextFieldWithView:subview];
  }
 }
 return nil;
}
@end

PS:關于如何查找自己的App項目是否使用了私有api,可以參考iOS查找私有API 文章

2. 模態彈窗 ViewController 默認樣式改變

模態彈窗屬性 UIModalPresentationStyle 在 iOS 13 下默認被設置為 UIModalPresentationAutomatic新特性,展示樣式更為炫酷,同時可用下拉手勢關閉模態彈窗。

若原有模態彈出 ViewController 時都已指定模態彈窗屬性,則可以無視該改動。

若想在 iOS 13 中繼續保持原有默認模態彈窗效果。可以通過 runtime 的 Method Swizzling 方法交換來實現。

#import "UIViewController+ChangeDefaultPresentStyle.h"

@implementation UIViewController (ChangeDefaultPresentStyle)

+ (void)load {

 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
  Class class = [self class];
  //替換方法
  SEL originalSelector = @selector(presentViewController:animated:completion:);
  SEL newSelector = @selector(new_presentViewController:animated:completion:);

  Method originalMethod = class_getInstanceMethod(class, originalSelector);
  Method newMethod = class_getInstanceMethod(class, newSelector);;
  BOOL didAddMethod =
  class_addMethod(class,
      originalSelector,
      method_getImplementation(newMethod),
      method_getTypeEncoding(newMethod));

  if (didAddMethod) {
   class_replaceMethod(class,
        newSelector,
        method_getImplementation(originalMethod),
        method_getTypeEncoding(originalMethod));

  } else {
   method_exchangeImplementations(originalMethod, newMethod);
  }
 });
}

- (void)new_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {

 viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
 [self new_presentViewController:viewControllerToPresent animated:flag completion:completion];
}

@end

3. 黑暗模式的適配

針對黑暗模式的推出,Apple官方推薦所有三方App盡快適配。目前并沒有強制App進行黑暗模式適配。因此黑暗模式適配范圍現在可采用以下三種策略:

  • 全局關閉黑暗模式

  • 指定頁面關閉黑暗模式

  • 全局適配黑暗模式

3.1. 全局關閉黑暗模式

方案一:在項目 Info.plist 文件中,添加一條內容,Key為 User Interface Style,值類型設置為String并設置為 Light 即可。

方案二:代碼強制關閉黑暗模式,將當前 window 設置為 Light 狀態。

if(@available(iOS 13.0,*)){
self.window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}

3.2 指定頁面關閉黑暗模式

從Xcode 11、iOS 13開始,UIViewController與View新增屬性 overrideUserInterfaceStyle,若設置View對象該屬性為指定模式,則強制該對象以及子對象以指定模式展示,不會跟隨系統模式改變。

  • 設置 ViewController 該屬性, 將會影響視圖控制器的視圖以及子視圖控制器都采用該模式

  • 設置 View 該屬性, 將會影響視圖及其所有子視圖采用該模式

  • 設置 Window 該屬性, 將會影響窗口中的所有內容都采用該樣式,包括根視圖控制器和在該窗口中顯示內容的所有控制器

3.3 全局適配黑暗模式

配黑暗模式,主要從兩方面入手:圖片資源適配與顏色適配

圖片資源適配

打開圖片資源管理庫 Assets.xcassets,選中需要適配的圖片素材item,打開最右側的 Inspectors 工具欄,找到 Appearances 選項,并設置為 Any, Dark模式,此時會在item下增加Dark Appearance,將黑暗模式下的素材拖入即可。關于黑暗模式圖片資源的加載,與正常加載圖片方法一致。

iOS13原生端適配的示例分析

顏色適配

iOS 13開始UIColor變為動態顏色,在Light Mode與Dark Mode可以分別設置不同顏色。若UIColor色值管理,與圖片資源一樣存儲于 Assets.xcassets 中,同樣參照上述方法適配。若UIColor色值并沒有存儲于 Assets.xcassets 情況下,自定義動態UIColor時,在iOS 13下初始化方法增加了兩個方法

+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

這兩個方法要求傳一個block,block會返回一個 UITraitCollection 類

當系統在黑暗模式與正常模式切換時,會觸發block回調

示例代碼:

UIColor *dynamicColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
  if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
   return [UIColor whiteColor];
  } else {
   return [UIColor blackColor];
  }
 }];
 
 [self.view setBackgroundColor:dynamicColor];

當然了,iOS 13系統也默認提供了一套基本的黑暗模式UIColor動態顏色,具體聲明如下:

@property (class, nonatomic, readonly) UIColor *systemBrownColor  API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemIndigoColor  API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemGray2Color  API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray3Color  API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray4Color  API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray5Color  API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGray6Color  API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *labelColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *secondaryLabelColor  API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *tertiaryLabelColor  API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *quaternaryLabelColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *linkColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *placeholderTextColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *separatorColor   API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *opaqueSeparatorColor API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@property (class, nonatomic, readonly) UIColor *systemBackgroundColor     API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemBackgroundColor   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemBackgroundColor   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemGroupedBackgroundColor   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemGroupedBackgroundColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *systemFillColor       API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *secondarySystemFillColor    API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *tertiarySystemFillColor     API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@property (class, nonatomic, readonly) UIColor *quaternarySystemFillColor    API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);

監聽模式的切換

當需要監聽系統模式發生變化并作出響應時,需要用到 ViewController 以下函數

// 注意:參數為變化前的traitCollection,改函數需要重寫
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
 
// 判斷兩個UITraitCollection對象是否不同
- (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;

示例代碼:

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
 [super traitCollectionDidChange:previousTraitCollection];
 // trait has Changed?
 if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
 // do something...
 }
 }

系統模式變更,自定義重繪視圖

當系統模式變更時,系統會通知所有的 View以及 ViewController 需要更新樣式,會觸發以下方法執行(參考Apple官方適配鏈接):

NSView

- (void)updateLayer;
- (void)drawRect:(NSRect)dirtyRect;
- (void)layout;
- (void)updateConstraints;

UIView

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)layoutSubviews;
- (void)drawRect:(NSRect)dirtyRect;
- (void)updateConstraints;
- (void)tintColorDidChange;

UIViewController

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)updateViewConstraints;
- (void)viewWillLayoutSubviews;
- (void)viewDidLayoutSubviews;

UIPresentationController

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)containerViewWillLayoutSubviews;
- (void)containerViewDidLayoutSubviews;

4. LaunchImage即將廢棄

使用 LaunchImage 設置啟動圖,需要提供各類屏幕尺寸的啟動圖適配,這種方式隨著各類設備尺寸的增加,增加了額外不必要的工作量。為了解決 LaunchImage 帶來的弊端,iOS 8引入了 LaunchScreen 技術,因為支持 AutoLayout + SizeClass,所以通過 LaunchScreen 就可以簡單解決適配當下以及未來各種屏幕尺寸。

Apple官方已經發出公告,2020年4月開始,所有使用iOS 13 SDK 的App都必須提供 LaunchScreen。創建一個 LaunchScreen 也非常簡單

(1)New Files創建一個 LaunchScreen,在創建的 ViewController 下 View 中新建一個 Image,并配置 Image 的圖片
(2)調整 Image 的 frame 為占滿屏幕,并修改 Image 的 Autoresizing 如下圖,完成

iOS13原生端適配的示例分析

5. 新增一直使用藍牙的權限申請

在iOS13之前,無需權限提示窗即可直接使用藍牙,但在iOS 13下,新增了使用藍牙的權限申請。最近一段時間上傳IPA包至App Store會收到以下提示。

iOS13原生端適配的示例分析

解決方案:只需要在 Info.plist 里增加以下條目:

<key>NSBluetoothAlwaysUsageDescription</key> 
<string>這里輸入使用藍牙來做什么</string>`

6. Sign With Apple

在iOS 13系統中,Apple要求提供第三方登錄的App也要支持「Sign With Apple」,具體實踐參考 iOS Sign With Apple實踐

7. 推送Device Token適配

在iOS 13之前,獲取Device Token 是將系統返回的 NSData 類型數據通過 -(void)description; 方法直接轉換成 NSString 字符串。

iOS 13之前獲取結果:

iOS13原生端適配的示例分析

iOS 13之后獲取結果:

iOS13原生端適配的示例分析

適配方案:目的是要將系統返回 NSData 類型數據轉換成字符串,再傳給推送服務方。-(void)description; 本身是用于為類調試提供相關的打印信息,嚴格來說,不應直接從該方法獲取數據并應用于正式環境中。將 NSData 轉換成 HexString,即可滿足適配需求。

- (NSString *)getHexStringForData:(NSData *)data {
 NSUInteger length = [data length];
 char *chars = (char *)[data bytes];
 NSMutableString *hexString = [[NSMutableString alloc] init];
 for (NSUInteger i = 0; i < length; i++) {
  [hexString appendString:[NSString stringWithFormat:@"%0.2hhx", chars[i]]];
 }
 return hexString;
}

8. UIKit 控件變化

主要還是參照了Apple官方的 UIKit 修改文檔聲明。iOS 13 Release Notes

8.1. UITableView

iOS 13下設置 cell.contentView.backgroundColor 會直接影響 cell 本身 selected 與 highlighted 效果。建議不要對 contentView.backgroundColor 修改,而對 cell 本身進行設置。

8.2. UITabbar

Badge 文字大小變化

iOS 13之后,Badge 字體默認由13號變為17號。建議在初始化 TabbarController 時,顯示 Badge 的 ViewController 調用 setBadgeTextAttributes:forState: 方法

if (@available(iOS 13, *)) {
 [viewController.tabBarItem setBadgeTextAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateNormal];
 [viewController.tabBarItem setBadgeTextAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateSelected];
}

8.2. UITabBarItem

加載gif需設置 scale 比例

NSData *data = [NSData dataWithContentsOfFile:path];
CGImageSourceRef gifSource = CGImageSourceCreateWithData(CFBridgingRetain(data), nil);
size_t gifCount = CGImageSourceGetCount(gifSource);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i,NULL);

// iOS 13之前
UIImage *image = [UIImage imageWithCGImage:imageRef]
// iOS 13之后添加scale比例(該imageView將展示該動圖效果)
UIImage *image = [UIImage imageWithCGImage:imageRef scale:image.size.width / CGRectGetWidth(imageView.frame) orientation:UIImageOrientationUp];

CGImageRelease(imageRef);

無文字時圖片位置調整

iOS 13下不需要調整 imageInsets,圖片會自動居中顯示,因此只需要針對iOS 13之前的做適配即可。

if (IOS_VERSION < 13.0) {
  viewController.tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0);
 }

8.3. 新增 Diffable DataSource

在 iOS 13下,對 UITableView 與 UICollectionView 新增了一套 Diffable DataSource API。為了更高效地更新數據源刷新列表,避免了原有粗暴的刷新方法 - (void)reloadData,以及手動調用控制列表刷新范圍的api,很容易出現計算不準確造成 NSInternalInconsistencyException 而引發App crash。
api 官方鏈接

9. StatusBar新增樣式

StatusBar 新增一種樣式,默認的 default 由之前的黑色字體,變為根據系統模式自動選擇展示 lightContent 或者 darkContent

針對iOS 13 SDK適配,后續將會持續收集并更新

感謝你能夠認真閱讀完這篇文章,希望小編分享的“iOS13原生端適配的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

ios
AI

无极县| 云霄县| 平泉县| 醴陵市| 兖州市| 定远县| 宣威市| 宜兰县| 衡水市| 水城县| 疏附县| 清苑县| 赤城县| 正蓝旗| 道真| 冀州市| 玛沁县| 乌鲁木齐县| 达孜县| 赞皇县| 拉孜县| 宣恩县| 开化县| 南投市| 兴文县| 铁力市| 西乡县| 安泽县| 元谋县| 乾安县| 宁乡县| 江华| 溧水县| 左贡县| 高州市| 抚远县| 兴山县| 桑植县| 武冈市| 阿拉善左旗| 丘北县|