1. 程式人生 > IOS開發 >iOS 13 適配 ING...

iOS 13 適配 ING...

內容宣告

赤裸裸的使用本文內容去做所謂原創的,麻煩要點臉。

Xcode11 缺失庫檔案匯入位置變更

libstdc-6.0.9 檔案下載

Xcode11下 這個目錄不存在了

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/
【變更為】
/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/
複製程式碼

----以下位置不需要改變

/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/
複製程式碼

友盟相關 升級最新版本SDK

註冊新浪平臺 崩潰【驗證:僅在模擬器上出現】

這個應該是需要微博官方進行適配了,嘗試模擬了 getUniqueStrByUUID 中的相關寫法。

螢幕快照2019-06-05下午1.47.08.png

  • 暫時的解決方案:
//修復iOS13下 崩潰問題 驗證為:模擬器下出現
#if TARGET_IPHONE_SIMULATOR
/// 交換方法實現
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        if(@available(iOS 13.0,*)){
            Method origin = class_getClassMethod([UIDevice class],NSSelectorFromString(@"getUniqueStrByUUID"
)); // IMP originImp = method_getImplementation(origin); Method swizz = class_getClassMethod([self class],@selector(swizz_getUniqueStrByUUID)); //交換方法實現 method_exchangeImplementations(origin,swizz); } }); #pragma mark - 獲取唯一標識 新浪 + (NSString *)swizz_getUniqueStrByUUID{ CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID //get the string representation of the UUID NSString *uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(nil,uuidObj); CFRelease(uuidObj); return uuidString ; } #endif 複製程式碼
+[_LSDefaults sharedInstance] 崩潰問題

針對的友盟版本: 移動統計:6.0.5 訊息推送:3.2.4 社會化分享:6.9.6

  • 暫時的解決方案:
@implementation NSObject (Extend)
+ (void)load{
    
    SEL originalSelector = @selector(doesNotRecognizeSelector:);
    SEL swizzledSelector = @selector(sw_doesNotRecognizeSelector:);
    
    Method originalMethod = class_getClassMethod(self,originalSelector);
    Method swizzledMethod = class_getClassMethod(self,swizzledSelector);
    
    if(class_addMethod(self,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod))){
        class_replaceMethod(self,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
    }else{
        method_exchangeImplementations(originalMethod,swizzledMethod);
    }
}

+ (void)sw_doesNotRecognizeSelector:(SEL)aSelector{
    //處理 _LSDefaults 崩潰問題
    if([[self description] isEqualToString:@"_LSDefaults"] && (aSelector == @selector(sharedInstance))){
        //冷處理...
        return;
    }
    [self sw_doesNotRecognizeSelector:aSelector];
}

複製程式碼

DeviceToken獲取 友盟公告

#include <arpa/inet.h>

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    if (![deviceToken isKindOfClass:[NSData class]]) return;
    const unsigned *tokenBytes = (const unsigned *)[deviceToken bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",ntohl(tokenBytes[0]),ntohl(tokenBytes[1]),ntohl(tokenBytes[2]),ntohl(tokenBytes[3]),ntohl(tokenBytes[4]),ntohl(tokenBytes[5]),ntohl(tokenBytes[6]),ntohl(tokenBytes[7])];
    NSLog(@"deviceToken:%@",hexToken);
}
複製程式碼

UITextField

通過KVC方式修改空白提示語顏色 崩潰
[UITextField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor”];
複製程式碼
  • 解決方案:
attributedPlaceholder
複製程式碼
leftView、rightView 設定異常 【疑似iOS13beta4新出現】

在設定leftView 左按鈕時,如果使用的是UIImageView即會出現圖片無法按照意圖顯示的問題。

@騲尼罵人獣狂 反饋UITextFieldrightView的子檢視如果使用約束佈局, 會導致rightView覆蓋整個UITextField

// Confuse in beta4 iOS13
UIImageView *imageIcon = [[UIImageView alloc]initWithFrame:CGRectMake(0,34,30)];
//search_icon  15*15
imageIcon.image = [UIImage imageNamed:@"search_icon"];
imageIcon.contentMode = UIViewContentModeCenter;
UITextField *txtSearch = [[UITextField alloc] init];
txtSearch.leftView = imageIcon;
複製程式碼
  • 解決方案:UIImageVIew 包一層UIView再設定給leftView 、設定leftView或rightView不要使用約束佈局

UISearchBar

驗證來源:@騲尼罵人獣狂 searchTextField屬性對外暴露了,不用再通過KVC獲取了。

UISearchBar *searchBar = [[UISearchBar alloc]init];
searchBar.searchTextField
複製程式碼

控制器相關

模態跳轉樣式變更

過場動畫上下文機制有調整,預設調整為了卡片樣式。

/*
 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 other system-provided view controllers may resolve UIModalPresentationAutomatic to other concrete presentation styles.
 Defaults to UIModalPresentationAutomatic on iOS starting in iOS 13.0,and UIModalPresentationFullScreen on previous versions. Defaults to UIModalPresentationFullScreen on all other platforms.
 */
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle API_AVAILABLE(ios(3.2));
[nav setModalPresentationStyle:UIModalPresentationFullScreen];
複製程式碼
模態橫屏彈出

如果希望模態檢視是以橫屏狀態彈出,需要注意到其會受到跳轉樣式的影響。 預設的卡片樣式,仍然會以豎屏彈出。

參考連結

- (BOOL)shouldAutorotate {
    return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
       return UIInterfaceOrientationMaskLandscapeRight;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        return UIInterfaceOrientationLandscapeRight;
}

複製程式碼

Dark Mode 顏色主題相關

UIColor 適配
 UIColor *dynamicColor =  [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * provider) {
 //使用 provider 判斷,有時會出問題
    if(keyWindow.isDark){
        return darkColor;
    }
    return lightColor;
 }];
複製程式碼
YYTextView && YYLabel 適配 完美解決的前提是 UIColor 必須正確適配

針對YY的改造--連結直達

如果你也在做該適配的話,那麼很可能遇到以下的問題。 嘗試了很多次,最大的問題竟出在 UIColor。 不過出現的問題,疑似和YY內部在Runloop 即將休眠時進行繪製任務具有很大的相關性。 具體原因還不能確定,等以後再深究一下。

原生控制元件的表現

這裡先看下系統UILabel的暗夜適配找尋一下靈感

UILabel DarkMode.png
可以看到,UILabel的繪製是呼叫 drawTextInRext,而翻看YY能看到其使用的 CTRunDraw()。由於一開始對UIDynamicProviderColor有誤解,也嘗試過解析其中打包的顏色,來通過檢視CTRun的屬性集來判斷當前是否正確渲染。

....然而,在YYLabel應用上述方案時可能正常,但YYTeView卻出現了其它的問題。 ....排查中發現,某些時候UITraitCollection.currentTraitCollection解析出的顏色,和對應的狀態不符。 ....最終發現,colorWithDynamicProvider中回撥的狀態可能出現和當前系統狀態不一致的情況,也就是說這個回撥有點不那麼可信了... 誤我青春

YYLabel 適配

YYLabel.m 新增如下程式碼

#pragma mark - DarkMode Adapater

#ifdef __IPHONE_13_0
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection{
    [super traitCollectionDidChange:previousTraitCollection];
    
    if (@available(iOS 13.0,*)) {
        if([UITraitCollection.currentTraitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]){
            [self.layer setNeedsDisplay];
        }
    } else {
        // Fallback on earlier versions
    }
}
#endif
複製程式碼
YYTextView 適配

YYTextView.m 新增如下程式碼

#pragma mark - Dark mode Adapter

#ifdef __IPHONE_13_0
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection{
    [super traitCollectionDidChange:previousTraitCollection];
    
    if (@available(iOS 13.0,*)) {
        if([UITraitCollection.currentTraitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]){
            [self _commitUpdate];
        }
    } else {
        // Fallback on earlier versions
    }
}
#endif
複製程式碼

額外要做的事情

  • NSAttributedString+YYText.m 去除部分CGColor 呼叫
- (void)setColor:(UIColor *)color {
    [self setColor:color range:NSMakeRange(0,self.length)];
}

- (void)setStrokeColor:(UIColor *)strokeColor {
    [self setStrokeColor:strokeColor range:NSMakeRange(0,self.length)];
}

- (void)setStrikethroughColor:(UIColor *)strikethroughColor {
    [self setStrikethroughColor:strikethroughColor range:NSMakeRange(0,self.length)];
}

- (void)setUnderlineColor:(UIColor *)underlineColor {
    [self setUnderlineColor:underlineColor range:NSMakeRange(0,self.length)];
}

複製程式碼

UIImageView

主要發現兩個問題:

1.初始化:UIImageView.image 初始化時必須設定,否則不顯示 。

2.暗黑適配:圖片經過拉伸處理後,會導致暗黑適配失效。

#pragma mark - 解決Image拉伸問題
+ (UITraitCollection *)lightTrait API_AVAILABLE(ios(13.0)) {
    static UITraitCollection *trait = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        trait = [UITraitCollection traitCollectionWithTraitsFromCollections:@[
            [UITraitCollection traitCollectionWithDisplayScale:UIScreen.mainScreen.scale],[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]
        ]];
    });

    return trait;
}

+ (UITraitCollection *)darkTrait API_AVAILABLE(ios(13.0)) {
    static UITraitCollection *trait = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]
        ]];
    });

    return trait;
}

+ (void)fixResizableImage API_AVAILABLE(ios(13.0)) {
    
    Class klass = UIImage.class;
    SEL selector = @selector(resizableImageWithCapInsets:resizingMode:);
    Method method = class_getInstanceMethod(klass,selector);
    if (method == NULL) {
        return;
    }
    
    IMP originalImp = class_getMethodImplementation(klass,selector);
    if (!originalImp) {
        return;
    }
    
    IMP dynamicColorCompatibleImp = imp_implementationWithBlock(^UIImage *(UIImage *_self,UIEdgeInsets insets,UIImageResizingMode resizingMode) {
            // 理論上可以判斷UIColor 是否是 UIDynamicCatalogColor.class,如果不是,直接返回原實現; 但沒必要.
            UITraitCollection *lightTrait = [self lightTrait];
            UITraitCollection *darkTrait = [self darkTrait];

            UIImage *resizable = ((UIImage * (*)(UIImage *,SEL,UIEdgeInsets,UIImageResizingMode))
                                      originalImp)(_self,selector,insets,resizingMode);
            UIImage *resizableInLight = [_self.imageAsset imageWithTraitCollection:lightTrait];
            UIImage *resizableInDark = [_self.imageAsset imageWithTraitCollection:darkTrait];
        
            if (resizableInLight) {
                [resizable.imageAsset registerImage:((UIImage * (*)(UIImage *,UIImageResizingMode))
                                                         originalImp)(resizableInLight,resizingMode)
                                withTraitCollection:lightTrait];
            }
            if (resizableInDark) {
                [resizable.imageAsset registerImage:((UIImage * (*)(UIImage *,UIImageResizingMode))
                                                         originalImp)(resizableInDark,resizingMode)
                                withTraitCollection:darkTrait];
            }
            return resizable;
        });

    class_replaceMethod(klass,dynamicColorCompatibleImp,method_getTypeEncoding(method));
}

複製程式碼

繪製異常 【暫未確定是否為iOS13新發】

UIImageView繪製

應用場景:自定義控制元件生成圖片,繪製API會受到UIImage的拉伸方式UIImageResizingModeTile干擾。

影響:所得並不是所見

/// 常見的繪製程式碼
UIGraphicsBeginImageContextWithOptions(contentSize,YES,[[UIScreen mainScreen] scale]);

問題API:
/// CGContextRef ctx = UIGraphicsGetCurrentContext();
/// [self.viewContent.layer renderInContext:ctx];

建議使用如下API:
[self.viewContent drawViewHierarchyInRect:CGRectMake(0,contentSize.width,contentSize.height) afterScreenUpdates:YES];

//生成圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
複製程式碼
截長圖

有朋友反饋 iOS13上截長圖,只能擷取到當前螢幕。 原因:約束和絕對佈局混用導致,截圖程式碼和佈局程式碼的UI設定環境調整為一致就好。

  • 隨處可見的截長圖程式碼,原理就是設定UIView.size為實際內容size
    UIImage* shotImage = nil;
    UITableView *scrollView = self.tableBase;
    //臨時資料
    CGPoint fltOriginOffset = scrollView.contentOffset;
    CGRect rectOrigin = scrollView.frame;

    scrollView.contentOffset = CGPointZero;
    scrollView.frame = CGRectMake(0,scrollView.contentSize.width,scrollView.contentSize.height);

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(scrollView.contentSize.width,scrollView.contentSize.height),scrollView.opaque,0.0);
    [scrollView.layer renderInContext:UIGraphicsGetCurrentContext()];
    shotImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    
    //狀態復原
    scrollView.contentOffset = fltOriginOffset;
    scrollView.frame = rectOrigin;
複製程式碼

UIWindow 變更 iOS 13.3 已由蘋果修復

在iOS13版本下,App 任意處生成YYTextView均會導致全域性的scrollsToTop 回頂功能失效。 雖然最終追查至YYTextEffectWindow中,但整個排查過程還是發現了很多新內容的。

這是正常的回頂功能呼叫邏輯,基於此一條條的覆寫了系統相關的私有函式來判別問題出自何處。


-[UICollectionView scrollViewShouldScrollToTop:] 
-[UIScrollView _scrollToTopIfPossible:] ()
-[UIScrollView _scrollToTopFromTouchAtScreenLocation:resultHandler:] ()
-[UIWindow _scrollToTopViewsUnderScreenPointIfNecessary:resultHandler:]_block_invoke.796 ()
-[UIWindow _handleScrollToTopAtXPosition:resultHandler:] ()

//此處能看到有個新鮮的 UIStatusBarManager 是iOS13新增的類,可以看到狀態列的點選事件已經被其接管了。
//經過實踐,出問題的時候該方法也能被正常呼叫故此排上以上呼叫棧方法。
-[UIStatusBarManager _handleScrollToTopAtXPosition:] ()
-[UIStatusBarManager handleTapAction:] ()
複製程式碼

開始以為是多個UIScrollView共存時scrollsToTop的設定問題,還有UIScrollViewContentInsetAdjustmentNever的設定問題。結果都不是... 最終沿著UIScrollView 子類一直查詢,找到了YYTextView 其中用到的 YYTextEffectWindow也進入了視野...

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        if (![UIApplication isAppExtension]) {
            one = [self new];
            one.frame = (CGRect){.size = kScreenSize};
            one.userInteractionEnabled = NO;
            //此處能看到 視窗等級是高於狀態列的,但多次嘗試等級調整均無果。
            one.windowLevel = UIWindowLevelStatusBar + 1;
            
           //元凶在這裡
           //所以,即使關閉了使用者互動 但是它竟能夠阻擋狀態列的事件,但卻對常規Window的事件無任何影響...
            if (@available(iOS 13.0,*)) {
                //費解的結果...
                one.hidden = YES;
            }else{
                one.hidden = NO;
            }
            
            // for iOS 9:
            one.opaque = NO;
            one.backgroundColor = [UIColor clearColor];
            one.layer.backgroundColor = [UIColor clearColor].CGColor;
        }
    });
    return one;
複製程式碼

UIWindow 上使用addSubview新增子檢視,需要注意 暗黑模式切換,並不會向子檢視下發狀態變更。

  • 解決方案:
獲取系統暗黑切換通知(自行實現),然後重寫新增到 UIWindow 的子檢視 overrideUserInterfaceStyle 為正確的狀態。
複製程式碼

UIScrollView 滾動條異常偏移

螢幕旋轉可能會觸發系統對滾動條的自動修正 如果沒有修改需求,關閉該特性即可

#ifdef __IPHONE_13_0
   if (@available(iOS 13.0,*)) {
       self.automaticallyAdjustsScrollIndicatorInsets = NO;
   }
#endif
複製程式碼

UICollectionView 異常API

該API 在iOS13下會強制將cell置中,導致上部留白。 推測,底部應該也會有留白的情況。

#pragma mark - 修復iOS13 下滾動異常API
#ifdef __IPHONE_13_0
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated{
    
    [super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];

    //修復13下 Cell滾動位置異常
   if (@available(iOS 13.0,*)) {
      //頂部
      if(self.contentOffset.y < 0){
          [self setContentOffset:CGPointZero];
          return;
      }
    
      //底部
      if(self.contentOffset.y > self.contentSize.height){
          [self setContentOffset:CGPointMake(0,self.contentSize.height)];
      }
  }
}
#endif
複製程式碼

UITableViewCell 異常 【疑似iOS13beta4新出現】

[self addSubView: viewObjA]

[self.contentView addSubview:viewObjB]

上面兩種方式,在遇到摺疊需求時。設定self.clipsToBounds=YES 可能會有佈局異常

  • 解決方案:
//有其他朋友反饋,遇到的情況正好相反。保險的做法 [新增如下兩句] 
self.clipsToBounds = YES;  【無效】
//此處很費解
self.layer.masksTobounds = YES;  【有效】
複製程式碼

疑似佈局引擎機制有調整 【有待確定】

在除錯中發現,如果程式碼流是這樣一種狀態 可能會造成 【約束失效】

如果遇到詭異問題 需要排查下是否存在約束和絕對佈局混用的情況

[viewMain addSubView:viewA];
[viewMain addSubView:viewB];
// 更新 viewA 的約束
 [self.imageBackground mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.mas_equalTo(50);
 }];

 //立即更新約束
 [viewA.superview setNeedsUpdateConstraints];
 [viewA.superview updateConstraintsIfNeeded];
 [viewA.superview layoutIfNeeded];

//更新容器約束
[viewMain mas_updateConstraints:^(MASConstraintMaker *make) {
       make....
 }];

....
其它處理邏輯
....

// 更新 viewA 的約束 【程式碼不會生效】
 [self.imageBackground mas_updateConstraints:^(MASConstraintMaker *make) {
       make.top.mas_equalTo(100);
 }];

複製程式碼

WKWebView

暗黑適配

要點:

  1. 模式引數通過 UA 傳遞
  2. 聯調中出現載入閃白問題
webView.opaque = false;
複製程式碼
WebJavascriptBridge失效
待驗證
複製程式碼

手勢影響

iOS13下,如果在UITextView上附加如拖動手勢,會發現如果觸點落在UITextView之上極易觸發第一響應者。 實際效果,可對比iOS12之前版本的表現。

!!!注意這兩項即使開啟也不會有改善效果!!!
gesture.cancelsTouchesInView = YES;
gesture.delaysTouchesBegan = YES;
複製程式碼
  • 先看一下相關呼叫棧
    單擊UITextView.png

iOS12下拖動UITextView.png

iOS13下拖動UITextView.png

  • 解決方案:為UITextView內部手勢新增外部依賴
///UITextView override
#ifdef __IPHONE_13_0
//用於解決 iOS13下,可拖動UITextView場景中放大鏡手勢引起的異常啟用編輯狀態問題。
-(void)addInteraction:(id<UIInteraction>)interaction{
    [super addInteraction:interaction];

    if(!self.otherGestureRecognizer || self.otherGestureRecognizer.count == 0){
        return;
    }

    if([interaction isKindOfClass:NSClassFromString(@"UITextLoupeInteraction")]){
        UIGestureRecognizer *delayLoupeGesture = [self.gestureRecognizers objectWithBlock:^BOOL(UIGestureRecognizer *obj) {
            return [obj isKindOfClass:NSClassFromString(@"UIVariableDelayLoupeGesture")];
        }];
        if(delayLoupeGesture){
            
            [self.otherGestureRecognizer forEach:^(UIGestureRecognizer *obj) {
                [delayLoupeGesture requireGestureRecognizerToFail:obj];
            }];
        }

    }
}
#endif
複製程式碼

iOS13 下文字框 -- 針對YYTextView

雙游標問題

注意:之前使用 __IPHONE_13_0 巨集錯誤,應使用 @available(iOS 13.0,*)

具體方法: 
_updateSelectionView {
...
...
}
複製程式碼

針對YYTextView所做的過往修改 連結直達

問題產生原因:

  1. 機制更改
  2. 之前所做的解決搜狗輸入法、系統鍵盤單詞中斷問題所做的修改存在問題。
  • 機制更改- 在真機上嘗試了很多次,確認如下: iOS13下,只要遵循了UITextInput相關協議,在進行文字選擇操作時系統會自動派生出UITextSelectionView系列元件,顯然和YY自有的YYTextSelectionView衝突了。(此處隱藏一顆彩蛋)

  • 之前對YYTextView所做的程式碼變更,武斷的註釋掉了如下方法:

if (notify) [_inputDelegate selectionWillChange:self];
if (notify) [_inputDelegate selectionDidChange:self];
複製程式碼

會造成內部對文字選擇相關的資料錯亂,當然這種影響目前只在iOS13下能夠看到表現。

  • 解決方案 :隱藏系統派生出來的UITextSelectionView相關元件
/// YYTextView.m 

/// 增加標記位
struct {
       .....
       unsigned int trackingDeleteBackward : 1;  ///< track deleteBackward operation
       unsigned int trackingTouchBegan : 1;  /// < track touchesBegan event
} _state;

/// 方法重寫
#pragma mark - override
- (void)addSubview:(UIView *)view{
    
    //解決藍點問題
    Class Cls_selectionGrabberDot = NSClassFromString(@"UISelectionGrabberDot");
    if ([view isKindOfClass:[Cls_selectionGrabberDot class]]) {
        view.backgroundColor = [UIColor clearColor];
        view.tintColor = [UIColor clearColor];
        view.size = CGSizeZero;
    }
    
    //獲取UITextSelectionView
    //解決雙游標問題
    Class Cls_selectionView = NSClassFromString(@"UITextSelectionView");

    if ([view isKindOfClass:[Cls_selectionView class]]) {
        view.backgroundColor = [UIColor clearColor];
        view.tintColor = [UIColor clearColor];
        view.hidden = YES;
    }
    
    [super addSubview:view];
    
}

/// 方法修改
/// Replace the range with the text,and change the `_selectTextRange`.
/// The caller should make sure the `range` and `text` are valid before call this method.
- (void)_replaceRange:(YYTextRange *)range withText:(NSString *)text notifyToDelegate:(BOOL)notify{
    if (_isExcludeNeed) {
        notify = NO;
    }

    if (NSEqualRanges(range.asRange,_selectedTextRange.asRange)) {
        //這裡的代理方法需要註釋掉 【廢止】
        //if (notify) [_inputDelegate selectionWillChange:self];
        /// iOS13 下,雙游標問題 便是由此而生。
        if (_state.trackingDeleteBackward)[_inputDelegate selectionWillChange:self];
        NSRange newRange = NSMakeRange(0,0);
        newRange.location = _selectedTextRange.start.offset + text.length;
        _selectedTextRange = [YYTextRange rangeWithRange:newRange];
        //if (notify) [_inputDelegate selectionDidChange:self];
        /// iOS13 下,雙游標問題 便是由此而生。
        if (_state.trackingDeleteBackward) [_inputDelegate selectionDidChange:self];
        ///恢復標記
        _state.trackingDeleteBackward = NO;
    } else {
    .....
    .....
}

- (void)deleteBackward {
    //標識出刪除動作:用於解決雙游標相關問題
    _state.trackingDeleteBackward = YES;
    
    [self _updateIfNeeded];
    .....
    .....
}

- (void)_updateSelectionView {
    _selectionView.frame = _containerView.frame;
    _selectionView.caretBlinks = NO;
    _selectionView.caretVisible = NO;
    _selectionView.selectionRects = nil;
  .....
  .....
    
    if (@available(iOS 13.0,*)) {
        if (_state.trackingTouchBegan) [_inputDelegate selectionWillChange:self];
        [[YYTextEffectWindow sharedWindow] showSelectionDot:_selectionView];
        if (_state.trackingTouchBegan) [_inputDelegate selectionDidChange:self];
    }else{
         [[YYTextEffectWindow sharedWindow] showSelectionDot:_selectionView];
    }

    if (containsDot) {
        [self _startSelectionDotFixTimer];
    } else {
        [self _endSelectionDotFixTimer];
  .....
  .....
}
複製程式碼
iOS13 新增編輯手勢 --暫時禁用

編輯手勢 描述借鑑 複製:三指捏合 剪下:兩次三指捏合 貼上:三指鬆開 撤銷:三指向左划動(或三指雙擊) 重做:三指向右划動 快捷選單:三指單擊

#ifdef __IPHONE_13_0
- (UIEditingInteractionConfiguration)editingInteractionConfiguration{
    return UIEditingInteractionConfigurationNone;
}
#endif
複製程式碼

UITabbar

UITabbar 層次發生改變,無法通過設定shadowImage去掉上面的線。 @還是老徐ooo 反饋

if #available(iOS 13,*) {
  let appearance = self.tabBar.standardAppearance.copy()
  appearance.backgroundImage = UIImage()
  appearance.shadowImage = UIImage()
  appearance.shadowColor = .clear
  self.tabBar.standardAppearance = appearance
} else {
  self.tabBar.shadowImage = UIImage()
  self.tabBar.backgroundImage = UIImage()
}
複製程式碼
UITabBar frame 設定失效
待驗證
複製程式碼

layoutSubviews 相關

//容錯性更高 
//具體待補充
複製程式碼

removeFromSuperview

除錯老專案偶然發現 SVProgressHUD在iOS13上會出現提示文案空白的問題,切換到iOS11以及12等版本後發現了該異常。

  • 相關程式碼段 SVProgressHUD
- (void)dismiss {
    NSDictionary *userInfo = [self notificationUserInfo];
    [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
                                                        object:nil
                                                      userInfo:userInfo];
    
    self.activityCount = 0;
    [UIView animateWithDuration:0.15
                          delay:0
                        options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
                     animations:^{
                         self.hudView.transform = CGAffineTransformScale(self.hudView.transform,0.8f,0.8f);
                         if(self.isClear) // handle iOS 7 UIToolbar not answer well to hierarchy opacity change
                             self.hudView.alpha = 0.0f;
                         else
                             self.alpha = 0.0f;
                     }
                     completion:^(BOOL finished){
                         if(self.alpha == 0.0f || self.hudView.alpha == 0.0f) {
                             self.alpha = 0.0f;
                             self.hudView.alpha = 0.0f;
                             
                             [[NSNotificationCenter defaultCenter] removeObserver:self];
                             [self cancelRingLayerAnimation];

                             [_hudView removeFromSuperview];
                             _hudView = nil;
                             
                             [_overlayView removeFromSuperview];
                             _overlayView = nil;
                             
                             [_indefiniteAnimatedView removeFromSuperview];
                             _indefiniteAnimatedView = nil;
                             
                             UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,nil);
                             
                             [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
                                                                                 object:nil
                                                                               userInfo:userInfo];
                             
                             // Tell the rootViewController to update the StatusBar appearance
#if !defined(SV_APP_EXTENSIONS)
                             UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
                             if ([rootController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
                                 [rootController setNeedsStatusBarAppearanceUpdate];
                             }
#endif
                             // uncomment to make sure UIWindow is gone from app.windows
                             //NSLog(@"%@",[UIApplication sharedApplication].windows);
                             //NSLog(@"keyWindow = %@",[UIApplication sharedApplication].keyWindow);
                         }
                     }];
}
......
......

- (UILabel *)stringLabel {
    if (!_stringLabel) {
        _stringLabel = [[UILabel alloc] initWithFrame:CGRectZero];
		_stringLabel.backgroundColor = [UIColor clearColor];
		_stringLabel.adjustsFontSizeToFitWidth = YES;
        _stringLabel.textAlignment = NSTextAlignmentCenter;
		_stringLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
        _stringLabel.numberOfLines = 0;
    }
    
    if(!_stringLabel.superview){
        [self.hudView addSubview:_stringLabel];
    }
    _stringLabel.textColor = SVProgressHUDForegroundColor;
    _stringLabel.font = SVProgressHUDFont;
    
    return _stringLabel;
}
複製程式碼

問題的實質原因在於,_stringLabel.superview 也就是 _hudView在呼叫[_hudView removeFromSuperview]後: iOS12以下 _stringLabel.superview = nil iOS13 _stringLabel.superview != nil

注意:以後如果使用view.superview 來進行判斷的話還是多思考一下吧

一斷毒程式碼 注:僅在iOS13以上版本可見

神奇表現 前提:系統旋轉開關處於鎖定狀態 DEV 除錯無任何問題 --- No Problem Adhoc 和 Release,只要使用者手動操作一次前後臺切換下面的方法即死翹翹。 --- Die 此問題為多種綜合因素造成,各位並不一定遇到。

#pragma mark - 旋轉螢幕
+(void)changeOrientation:(UIInterfaceOrientation)toOrientation{
 
  if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDeviceinstanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val = UIInterfaceOrientationLandscapeRight;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}
複製程式碼
  • 解決方案: 疑似函式內部棧指標錯亂
要解決看起來很簡單,只需要在方法函式頭部新增幾句不會被編譯器優化掉的程式碼即可。

#pragma mark - 旋轉螢幕
+(void)changeOrientation:(UIInterfaceOrientation)toOrientation{
  
  [self uploadLog:@"旋轉螢幕"];

  if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDeviceinstanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val = UIInterfaceOrientationLandscapeRight;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}
複製程式碼

狀態列相關

狀態列的顯示與隱藏

【方案一】

雖然下面的方法被列為DEPRECATED,在iOS13上仍然奏效。

- (void)setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation API_DEPRECATED("Use -[UIViewController prefersStatusBarHidden]",ios(3.2,9.0)) API_UNAVAILABLE(tvos);
複製程式碼

【方案二】

奇淫技巧:通過複寫私有方法,其實可以做到全域性狀態列不顯示,但並不推薦。

!!!還是不要嘗試的好!!!
複製程式碼
獲取狀態列 注意本質為新建立一個狀態列,和當前系統狀態列可能不一致
        if (@available(iOS 13.0,*)) {
            UIView *_localStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
            UIView * statusBar = [_localStatusBar performSelector:@selector(statusBar)];
            // 注意此程式碼不生效 用於繪製
           // [statusBar drawViewHierarchyInRect:statusBar.bounds afterScreenUpdates:NO];
            [statusBar.layer renderInContext:context];

        } else {
            // Fallback on earlier versions
        }
複製程式碼
橫屏時狀態列顯示且在左側或右側

常見於視訊播放頁需求 原因1:視訊播放器所屬的 UIViewController 未能正確旋轉所致。 原因2:iOS13下API失效 [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];

  • 解決方案:
允許控制器旋轉,iOS13橫屏時會自動隱藏狀態列。
複製程式碼
  • 狀態列設定背景色

由於iOS13下當前狀態列實際上被系統強制接管了,是拿不到的。? 或者是有我沒找到的API

  • 解決方案:
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    UIView *viewStatusColorBlend = [[UIView alloc]initWithFrame:keyWindow.windowScene.statusBarManager.statusBarFrame];
    viewStatusColorBlend.backgroundColor = Color;
    [keyWindow addSubview:viewStatusColorBlend];
複製程式碼
橫屏時顯示狀態列

【方案一】

針對iOS13 需要明確如下:

  1. iOS13 裝置支援情況,不同裝置有著不同的表現。
  2. 該方案不能保證一定通過蘋果稽核,請慎重對待!!! 請慎重對待!!! 請慎重對待!!!
  3. 建立了本地狀態列後,是會對系統的狀態列造成不確定影響的,所以下面的方法謹慎的使用了 addSubViewremoveFromSuperview

iOS13不支援的裝置.jpg

  • 狀態列在不同裝置的表現

A: iPhone X ~ iPhone ... Max (劉海屏):狀態列顯示時,分左、右兩段,其中左上角為時間、右上角為?等資訊,且在iOS13 下橫屏不顯示

B: iPhone7 ~ iPhone 8P (矩形屏):狀態列顯示時,分左、中、右三段,其中時間居中顯示,且在iOS13 下橫屏不顯示

C: iPAD (糊臉屏):狀態列顯示時,分左、右兩段,其中左上角為時間,右上角為?等資訊,且在iOS13 (糾正:iPad OS13)下預設顯示

總結: A 、B 兩種情況需要特殊處理

  • 效果預覽

Simulator Screen Shot - iPhone Xʀ - 2019-09-27 at 16.51.25.png

Simulator Screen Shot - iPhone 8 - 2019-09-27 at 16.54.08.png

  • 解決方案: 參考上面的獲取狀態列方法
/** 狀態列--iOS13 */
@property (nonatomic,strong) UIView  *viewStatusBar API_AVAILABLE(ios(13.0));

- (UIView *)viewStatusBar{
    if (!_viewStatusBar) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        _viewStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
        _viewStatusBar.backgroundColor = [UIColor clearColor];
        _viewStatusBar.hidden = YES;
        _viewStatusBar.tag = 784321;
        _viewStatusBar.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
        
        //手動設定狀態列為白色樣式
        UIView *statusBar = [[_viewStatusBar valueForKey:@"_statusBar"]valueForKey:@"_statusBar"];
        [statusBar performSelector:@selector(setForegroundColor:) withObject:[UIColor whiteColor]];
#pragma clang diagnostic pop
    }
    return _viewStatusBar;
}

#param mark - 橫屏方法
- (void)setOrientationLandscapeConstraint {
 //狀態列處理
    if (@available(iOS 13.0,*)) {
        if(![self viewWithTag:784321] && !DP_IS_IPAD){
            self.viewStatusBar.hidden = NO;
            [self addSubview:self.viewStatusBar];
            //我這裡是為了避開 其它控制元件,具體的按各自的需求來
            CGFloat fltLeftMargin = DP_IS_IPHONEX ? (-24) : 0;
            CGFloat fltTopMargin = DP_IS_IPHONEX ? (-12) : 0;
            UIView *leftView = DP_IS_IPHONEX ? self.lblTitle : self;
            
            [self.viewStatusBar mas_makeConstraints:^(MASConstraintMaker *make) {
                 make.left.mas_equalTo(leftView.mas_left).offset(fltLeftMargin);
                 make.top.mas_equalTo(fltTopMargin);
                 make.right.mas_equalTo(0);
            }];
        }
    }
}

#param mark - 豎屏方法
- (void)setOrientationPortraitConstraint { 
    //狀態列處理
    if (@available(iOS 13.0,*)) {
        if (!DP_IS_IPAD) {
            self.viewStatusBar.hidden = YES;
            if ([self viewWithTag:784321]) {
                [self.viewStatusBar removeFromSuperview];
            }
        }
    }
}

複製程式碼
  • 本來希望是達到和愛奇藝一樣的效果,但上面的方式如果順利過審的話已經滿足需求了。 當然 如果哪位大佬知道怎麼實現的,還望不吝賜教。

愛奇藝 XS Max 擷取

【方案二】重點:顯示系統狀態列

該方案使用原生狀態列,而非通過私有API建立。 額外注意的是:雖然能夠正常顯示,但對狀態列Frame修改的嘗試均失敗。

@implementation UIStatusBarManager (Extend)
/// 更改預設配置
- (CGFloat)defaultStatusBarHeightInOrientation:(UIInterfaceOrientation)orientation {
    if (orientation ==  UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight ) {
    /// 主要就是這裡    
    return 20;
    }
    /// 此處僅做示例,實際使用需要針對不同裝置進行適配。
    return 44;
}

@end
複製程式碼

【方案三】 印證輔助:Alook瀏覽器內建的視訊播放器和愛奇藝的效果基本一致,作者明確了橫屏狀態列是自實現,而非系統。

分析愛奇藝的實現,個人鄙見:主要講實現方案

  1. 視訊最終頁構成:僅支援豎屏的控制器 + 自定義承載視訊播放器的UIWindow
  2. 橫豎屏邏輯:通過彈出一個模態控制器,來控制真是的旋轉方向,但其內容一定是透明的。(具體大概意思就是 視訊最終頁(豎屏) ---> 透明的模態控制器(橫屏))
  3. 視訊播放器UIWindow轉場效果
  • 注意:該API已經失效,這也是很多人一直在找橫豎屏替代方案的原因。
 [[UIApplication sharedApplication] setStatusBarOrientation:currentOrientation animated:NO];
複製程式碼
  • 除錯時發現的一個私有方法:猜測是用來代替上面方法的,但實際呼叫無效果。
setStatusBarOrientation:fromOrientation:windowScene:animationParameters:updateBlock:
複製程式碼

【方案三】使用 iPAPatch 分析愛奇藝實現而來

橫屏時的電量與時間顯示,CustomUIView沒有疑問 。

視訊橫豎屏切換,使用的也是新建 CustomUIWindow,只不過其rootViewControllerPush而非模態

大體思路: CustomUIWindow 接替原UIWindow成為keyWindow,轉而使用其rootViewController控制當前螢幕的實際方向。

  • 驗證程式碼:實際效果 等同於 [[UIApplication sharedApplication] setStatusBarOrientation:currentOrientation animated:NO];

掃描 2019年12月18日 下午7.21.png

//切換橫豎屏方法 示例
+(void)changeOrientation:(UIInterfaceOrientation)toOrientation{
    UIApplication * app = [UIApplication sharedApplication];
    UIWindow * appWindow = app.delegate.window;
    //大小隨意
    CGRect rect = CGRectMake(0,30,30);
    //這裡其實可以 將toOrientation 傳遞給 TempViewController 用於

    if (!_customWindow) {
        _customWindow = [[UIWindow alloc] initWithFrame:rect];
        _customWindow.backgroundColor = [UIColor cyanColor];
        TempViewController * viewctrl = [[TempViewController alloc] init];
         _customWindow.rootViewController = viewctrl;
    }
   //其實並不一定要接替keyWindow,只需保證多Window中僅有一個能夠支援旋轉即可。
   //[_customWindow makeKeyAndVisible];
}
複製程式碼
#import "TempViewController.h"

@interface TempViewController ()

@end

@implementation TempViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor greenColor];
    
    UIView *viewRed = [[UIView alloc]initWithFrame:CGRectMake(0,10,10)];
    viewRed.backgroundColor = [UIColor redColor];
    [self.view addSubview:viewRed];
    
}

- (BOOL)shouldAutorotate{
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}


@end
複製程式碼
小結:關於橫豎屏以及狀態列的問題,總結至此。

程式碼規範問題相關 NSDate

命名

@frankfan1990 反饋應用切換到後臺時Crash,全域性斷點捕獲不到並且直接斷進了main函式。

問題原因: 自行實現的NSDatecategory,裡面有一個方法叫 +(NSInterger)now的方法。該方法在iOS13 中已有實現,具體崩潰原因待驗證。

解決方案: 改寫系統提供類的方法或者新增方法,注意避免衝突。最好將命名帶有前後綴,對於屬性也一樣適用。

奇淫巧技請注意

三目運算子 result = x?x:y; ==> result = x?:y;

可能是由於Xcode11 編譯器或者語法器的調整,上面的技巧可能不會得到你想要的結果了。
複製程式碼

VOIP 相關

@2742810c4ba1 反饋到後臺收不到推送,並且有報錯閃退。

解決方案: @叫點什麼好呢 提到push 推送的data資料結構發生變更,需要更新SDK。

3D Touch && Haptic Touch

支援判斷

注意:使用該判斷,並且直接return的話  可能你同時也就錯過了 Haptic Touch。
traitCollection.forceTouchCapability == UIForceTouchCapabilityUnavailable

可能是由於蘋果的疏忽,Haptic Touch的能力並未和3D Touch統一。
我能想到的唯一判斷方式,是通過系統版本。
//是否支援觸控力度檢測
if(self.window.traitCollection.forceTouchCapability == UIForceTouchCapabilityUnavailable){
    //不支援觸控力度檢測時,檢測是否支援 haptic touch。
    //haptic touch,判斷依據 僅iOS 13.0 以上版本。
    if(@available(iOS 13.0,*)) {
     
     }else{
            return;
     }
} 
複製程式碼

補充

狀態列除錯補充

在除錯過程中,挖了一些相關資料。 下面會貼出來,目前對於狀態列問題的解決並不是很滿意。 所以希望有興趣的朋友,能夠一起找尋下最佳方案。

配置及結構
  • 為什麼iOS13 橫屏會不顯示狀態列?

    預設配置.png

  • 狀態列層級結構

    狀態列層級結構.png

私有方法挖掘
  • UIStatusBarManager
.cxx_destruct
setWindowScene:
_settingsDiffActionsForScene:
initWithScene:
_scene
_setScene:
windowScene
isStatusBarHidden
defaultStatusBarHeightInOrientation:
statusBarStyle
statusBarHeight
updateStatusBarAppearance
updateLocalStatusBars
statusBarHidden
statusBarAlpha
setupForSingleLocalStatusBar
statusBarFrameForStatusBarHeight:
updateStatusBarAppearanceWithAnimationParameters:
_updateStatusBarAppearanceWithClientSettings:transitionContext:animationParameters:
_updateVisibilityForWindow:targetOrientation:animationParameters:
_updateStyleForWindow:animationParameters:
_updateAlpha
_visibilityChangedWithOriginalOrientation:targetOrientation:animationParameters:
activateLocalStatusBar:
_updateLocalStatusBar:
statusBarFrame
_handleScrollToTopAtXPosition:
_adjustedLocationForXPosition:
updateStatusBarAppearanceWithClientSettings:transitionContext:
deactivateLocalStatusBar:
createLocalStatusBar
handleTapAction:
localStatusBars
setLocalStatusBars:
statusBarPartStyles
isInStatusBarFadeAnimation
debugMenuHandler
setDebugMenuHandler:
複製程式碼
  • UIStatusBar_Modern
.cxx_destruct
 layoutSubviews
 intrinsicContentSize
 setMode:
 setOrientation:
 currentStyle
 forceUpdate:
 statusBar
 setStatusBar:
 forceUpdateData:
 setEnabledPartIdentifiers:
 setAvoidanceFrame:
 frameForPartWithIdentifier:
 alphaForPartWithIdentifier:
 setAlpha:forPartWithIdentifier:
 setOffset:forPartWithIdentifier:
 jiggleLockIcon
 setForegroundColor:animationParameters:
 setStyleRequest:animationParameters:
 enabledPartIdentifiers
 setAction:forPartWithIdentifier:
 statusBarServer:didReceiveStatusBarData:withActions:
 statusBarServer:didReceiveStyleOverrides:
 statusBarServer:didReceiveDoubleHeightStatusString:forStyle:
 statusBarStateProvider:didPostStatusBarData:withActions:
 defaultDoubleHeight
 setForegroundAlpha:animationParameters:
 _effectiveStyleFromStyle:
 actionForPartWithIdentifier:
 offsetForPartWithIdentifier:
 _initWithFrame:showForegroundView:wantsServer:inProcessStateProvider:
 setLegibilityStyle:animationParameters:
 _requestStyle:partStyles:animationParameters:forced:
 _implicitStyleOverrideForStyle:
 _effectiveDataFromData:activeOverride:canUpdateBackgroundActivity:
 _updateWithData:force:
 _requestStyle:partStyles:legibilityStyle:foregroundColor:animationParameters:
 _updateSemanticContentAttributeFromLegacyData:
 _dataFromLegacyData:
複製程式碼
  • _UIStatusBarLocalView
.cxx_destruct
initWithFrame:
willMoveToWindow:
statusBar
setStatusBar:
複製程式碼
  • _UIStatusBar
description
.cxx_destruct
setAction:
action
layoutSubviews
intrinsicContentSize
mode
initWithStyle:
setMode:
items
style
orientation
setOrientation:
foregroundColor
containerView
_updateDisplayedItemsWithData:styleAttrib
updateConstraints
setStyle:
gestureRecognizerShouldBegin:
currentData
traitCollectionDidChange:
setItems:
setForegroundColor:
areAnimationsEnabled
setSemanticContentAttribute:
updateWithData:
setStyleAttributes:
styleAttributes
itemWithIdentifier:
regions
_traitCollectionForChildEnvironment:
_accessibilityHUDGestureManager:HUDItemFo
_accessibilityHUDGestureManager:gestureLi
_accessibilityHUDGestureManager:shouldRec
_accessibilityHUDGestureManager:shouldTer
_accessibilityHUDGestureManager:showHUDIt
_dismissAccessibilityHUDForGestureManager
foregroundView
actionGestureRecognizer
avoidanceFrame
setEnabledPartIdentifiers:
setAvoidanceFrame:
frameForPartWithIdentifier:
alphaForPartWithIdentifier:
setAlpha:forPartWithIdentifier:
setOffset:forPartWithIdentifier:
enabledPartIdentifiers
setOverlayData:
setAction:forPartWithIdentifier:
setStyle:forPartWithIdentifier:
overlayData
updateCompletionHandler
setUpdateCompletionHandler:
setForegroundView:
visualProvider
_forceLayoutEngineSolutionInRationalEdges
resizeSubviewsWithOldSize:
currentAggregatedData
updateWithAnimations:
styleAttributesForStyle:
displayItemIdentifiersInRegionsWithIdenti
frameForDisplayItemWithIdentifier:
frameForPartWithIdentifier:includeInterna
dataAggregator
displayItemWithIdentifier:
regionWithIdentifier:
stateForDisplayItemWithIdentifier:
setDisplayItemStates:
_updateWithAggregatedData:
statusBarGesture:
_setVisualProviderClass:
_visualProviderClass
_prepareVisualProviderIfNeeded
_effectiveTargetScreen
_updateStyleAttributes
_performWithInheritedAnimation:
_effectiveStyleFromStyle:
_updateWithData:completionHandler:
_prepareAnimations:
_performAnimations:
_fixupDisplayItemAttributes
_delayUpdatesWithKeys:fromAnimation:
_updateRegionItems
_rearrangeOverflowedItems
_frameForActionable:actionInsets:
_gestureRecognizer:touchInsideActionable:
_gestureRecognizer:pressInsideActionable:
_frameForActionable:
_pressFrameForActionable:
_gestureRecognizer:isInsideActionable:
_regionsForPartWithIdentifier:includeInte
_actionablesForPartWithIdentifier:include
_itemWithIdentifier:createIfNeeded:
_statusBarWindowForAccessibilityHUD
_setVisualProviderClassName:
_visualProviderClassName
resetVisualProvider
actionForPartWithIdentifier:
offsetForPartWithIdentifier:
styleForPartWithIdentifier:
dependentDataEntryKeys
animationContextId
itemsDependingOnKeys:
itemIdentifiersInRegionsWithIdentifiers:
dataEntryKeysForItemsWithIdentifiers:
targetScreen
setTargetScreen:
displayItemStates
targetActionable
setTargetActionable:
accessibilityHUDGestureManager
setAccessibilityHUDGestureManager:
複製程式碼
相關資料 看了這些就理解了別人是怎麼挖到資訊的
(lldb) po [statusBar performSelector:@selector(currentData)]
<_UIStatusBarData: 0x7fee29fdada0: 
mainBatteryEntry=<_UIStatusBarDataBatteryEntry: 0x600001713c90: isEnabled=1,capacity=100,state=2,saverModeActive=0,prominentlyShowsDetailString=0,detailString=100%>,secondaryCellularEntry=<_UIStatusBarDataCellularEntry: 0x600003c4d9e0: isEnabled=1,rawValue=0,displayValue=0,displayRawValue=0,status=0,lowDataModeActive=0,type=5,wifiCallingEnabled=0,callForwardingEnabled=0,showsSOSWhenDisabled=0>,backNavigationEntry=<_UIStatusBarDataStringEntry: 0x60000191e540: isEnabled=0>,vpnEntry=<_UIStatusBarDataEntry: 0x600001aae360: isEnabled=0>,radarEntry=<_UIStatusBarDataBoolEntry: 0x600001aae280: isEnabled=0,boolValue=0>,rotationLockEntry=<_UIStatusBarDataEntry: 0x600001aadf20: isEnabled=0>,dateEntry=<_UIStatusBarDataStringEntry: 0x60000191e560: isEnabled=1,stringValue=Thu Sep 26>,quietModeEntry=<_UIStatusBarDataBoolEntry: 0x600001aae250: isEnabled=0,timeEntry=<_UIStatusBarDataStringEntry: 0x60000191e580: isEnabled=1,stringValue=5:15 PM>,personNameEntry=<_UIStatusBarDataStringEntry: 0x60000191e5a0: isEnabled=0>,cellularEntry=<_UIStatusBarDataCellularEntry: 0x600003c4da40: isEnabled=1,status=1,string=Carrier,assistantEntry=<_UIStatusBarDataEntry: 0x600001aae210: isEnabled=0>,bluetoothEntry=<_UIStatusBarDataBluetoothEntry: 0x60000191e5c0: isEnabled=0,state=0>,ttyEntry=<_UIStatusBarDataEntry: 0x600001aacbc0: isEnabled=0>,voiceControlEntry=<_UIStatusBarDataVoiceControlEntry: 0x60000191e5e0: isEnabled=0,type=0>,carPlayEntry=<_UIStatusBarDataEntry: 0x600001aacc00: isEnabled=0>,wifiEntry=<_UIStatusBarDataWifiEntry: 0x6000003b2880: isEnabled=1,displayValue=3,status=5,liquidDetectionEntry=<_UIStatusBarDataEntry: 0x600001aae160: isEnabled=0>,shortTimeEntry=<_UIStatusBarDataStringEntry: 0x60000191e600: isEnabled=1,stringValue=5:15>,studentEntry=<_UIStatusBarDataEntry: 0x600001aaeac0: isEnabled=0>,tetheringEntry=<_UIStatusBarDataTetheringEntry: 0x60000191e620: isEnabled=0,connectionCount=0>,alarmEntry=<_UIStatusBarDataEntry: 0x600001aae0e0: isEnabled=0>,activityEntry=<_UIStatusBarDataActivityEntry: 0x60000191e640: isEnabled=0,type=0,displayId=com.driver.feng>,locationEntry=<_UIStatusBarDataLocationEntry: 0x60000191e660: isEnabled=0,type=1>,airPlayEntry=<_UIStatusBarDataEntry: 0x600001aadfc0: isEnabled=0>,deviceNameEntry=<_UIStatusBarDataStringEntry: 0x60000191e680: isEnabled=0>,lockEntry=<_UIStatusBarDataLockEntry: 0x60000191e6a0: isEnabled=0,unlockFailureCount=0>,electronicTollCollectionEntry=<_UIStatusBarDataBoolEntry: 0x600001aae030: isEnabled=0,thermalEntry=<_UIStatusBarDataThermalEntry: 0x60000191e6c0: isEnabled=0,color=0,sunlightMode=0>,backgroundActivityEntry=<_UIStatusBarDataBackgroundActivityEntry: 0x600001713ed0: isEnabled=0,forwardNavigationEntry=<_UIStatusBarDataStringEntry: 0x60000191e700: isEnabled=0>,airplaneModeEntry=<_UIStatusBarDataEntry: 0x600001aadfa0: isEnabled=0>>

複製程式碼
(lldb) po [statusBar performSelector:@selector(items)]
{
    "<_UIStatusBarIdentifier: 0x600001907300: object=_UIStatusBarIndicatorTTYItem>" = "<_UIStatusBarIndicatorTTYItem: 0x6000003b1d40: identifier=<_UIStatusBarIdentifier: 0x600001907300: object=_UIStatusBarIndicatorTTYItem>>";
    "<_UIStatusBarIdentifier: 0x600001903a00: object=_UIStatusBarTimeItem>" = "<_UIStatusBarTimeItem: 0x600003b7f610: identifier=<_UIStatusBarIdentifier: 0x600001903a00: object=_UIStatusBarTimeItem>>";
    "<_UIStatusBarIdentifier: 0x600001907380: object=_UIStatusBarIndicatorAlarmItem>" = "<_UIStatusBarIndicatorAlarmItem: 0x6000003b1e00: identifier=<_UIStatusBarIdentifier: 0x600001907380: object=_UIStatusBarIndicatorAlarmItem>>";
    "<_UIStatusBarIdentifier: 0x600001906f80: object=_UIStatusBarSpacerItem>" = "<_UIStatusBarSpacerItem: 0x60000177ea00: identifier=<_UIStatusBarIdentifier: 0x600001906f80: object=_UIStatusBarSpacerItem>>";
    "<_UIStatusBarIdentifier: 0x600001903d60: object=_UIStatusBarActivityItem_Split>" = "<_UIStatusBarActivityItem_Split: 0x6000003b2000: identifier=<_UIStatusBarIdentifier: 0x600001903d60: object=_UIStatusBarActivityItem_Split>>";
    "<_UIStatusBarIdentifier: 0x600001903960: object=_UIStatusBarCellularCondensedItem>" = "<_UIStatusBarCellularCondensedItem: 0x600002aa8ea0: identifier=<_UIStatusBarIdentifier: 0x600001903960: object=_UIStatusBarCellularCondensedItem>>";
    "<_UIStatusBarIdentifier: 0x600001903a80: object=_UIStatusBarVoiceControlPillItem>" = "<_UIStatusBarVoiceControlPillItem: 0x600003b7f980: identifier=<_UIStatusBarIdentifier: 0x600001903a80: object=_UIStatusBarVoiceControlPillItem>>";
    "<_UIStatusBarIdentifier: 0x600001907400: object=_UIStatusBarIndicatorRotationLockItem>" = "<_UIStatusBarIndicatorRotationLockItem: 0x6000003b1e40: identifier=<_UIStatusBarIdentifier: 0x600001907400: object=_UIStatusBarIndicatorRotationLockItem>>";
    "<_UIStatusBarIdentifier: 0x600001907000: object=_UIStatusBarBluetoothItem>" = "<_UIStatusBarBluetoothItem: 0x6000003b1f00: identifier=<_UIStatusBarIdentifier: 0x600001907000: object=_UIStatusBarBluetoothItem>>";
    "<_UIStatusBarIdentifier: 0x600001906ee0: object=_UIStatusBarIndicatorAirplaneModeItem>" = "<_UIStatusBarIndicatorAirplaneModeItem: 0x6000003b2080: identifier=<_UIStatusBarIdentifier: 0x600001906ee0: object=_UIStatusBarIndicatorAirplaneModeItem>>";
    "<_UIStatusBarIdentifier: 0x600001907760: object=_UIStatusBarBuildVersionItem>" = "<_UIStatusBarBuildVersionItem: 0x60000177e310: identifier=<_UIStatusBarIdentifier: 0x600001907760: object=_UIStatusBarBuildVersionItem>>";
    "<_UIStatusBarIdentifier: 0x600001906d20: object=_UIStatusBarIndicatorVPNItem>" = "<_UIStatusBarIndicatorVPNItem: 0x6000003b1fc0: identifier=<_UIStatusBarIdentifier: 0x600001906d20: object=_UIStatusBarIndicatorVPNItem>>";
    "<_UIStatusBarIdentifier: 0x600001907480: object=_UIStatusBarIndicatorQuietModeItem>" = "<_UIStatusBarIndicatorQuietModeItem: 0x6000003b1e80: identifier=<_UIStatusBarIdentifier: 0x600001907480: object=_UIStatusBarIndicatorQuietModeItem>>";
    "<_UIStatusBarIdentifier: 0x6000019075a0: object=_UIStatusBarActivityItem_SyncOnly>" = "<_UIStatusBarActivityItem_SyncOnly: 0x60000177dad0: identifier=<_UIStatusBarIdentifier: 0x6000019075a0: object=_UIStatusBarActivityItem_SyncOnly>>";
    "<_UIStatusBarIdentifier: 0x6000019076c0: object=_UIStatusBarIndicatorLiquidDetectionItem>" = "<_UIStatusBarIndicatorLiquidDetectionItem: 0x6000003b1f40: identifier=<_UIStatusBarIdentifier: 0x6000019076c0: object=_UIStatusBarIndicatorLiquidDetectionItem>>";
    "<_UIStatusBarIdentifier: 0x600001907080: object=_UIStatusBarThermalItem>" = "<_UIStatusBarThermalItem: 0x6000003b1c80: identifier=<_UIStatusBarIdentifier: 0x600001907080: object=_UIStatusBarThermalItem>>";
    "<_UIStatusBarIdentifier: 0x600001906dc0: object=_UIStatusBarSecondaryCellularExpandedItem>" = "<_UIStatusBarSecondaryCellularExpandedItem: 0x60000366c380: identifier=<_UIStatusBarIdentifier: 0x600001906dc0: object=_UIStatusBarSecondaryCellularExpandedItem>>";
    "<_UIStatusBarIdentifier: 0x600001907500: object=_UIStatusBarIndicatorLocationItem>" = "<_UIStatusBarIndicatorLocationItem: 0x6000003b1ec0: identifier=<_UIStatusBarIdentifier: 0x600001907500: object=_UIStatusBarIndicatorLocationItem>>";
    "<_UIStatusBarIdentifier: 0x600001907100: object=_UIStatusBarIndicatorAssistantItem>" = "<_UIStatusBarIndicatorAssistantItem: 0x6000003b1cc0: identifier=<_UIStatusBarIdentifier: 0x600001907100: object=_UIStatusBarIndicatorAssistantItem>>";
    "<_UIStatusBarIdentifier: 0x600001907620: object=_UIStatusBarBatteryItem>" = "<_UIStatusBarBatteryItem: 0x600003b7f4d0: identifier=<_UIStatusBarIdentifier: 0x600001907620: object=_UIStatusBarBatteryItem>>";
    "<_UIStatusBarIdentifier: 0x600001903de0: object=_UIStatusBarNavigationItem>" = "<_UIStatusBarNavigationItem: 0x60000177fc90: identifier=<_UIStatusBarIdentifier: 0x600001903de0: object=_UIStatusBarNavigationItem>>";
    "<_UIStatusBarIdentifier: 0x600001907180: object=_UIStatusBarIndicatorAirPlayItem>" = "<_UIStatusBarIndicatorAirPlayItem: 0x6000003b1d00: identifier=<_UIStatusBarIdentifier: 0x600001907180: object=_UIStatusBarIndicatorAirPlayItem>>";
    "<_UIStatusBarIdentifier: 0x600001906c60: object=_UIStatusBarWifiItem>" = "<_UIStatusBarWifiItem: 0x6000003b2480: identifier=<_UIStatusBarIdentifier: 0x600001906c60: object=_UIStatusBarWifiItem>>";
    "<_UIStatusBarIdentifier: 0x600001903b60: object=_UIStatusBarVoiceControlItem>" = "<_UIStatusBarVoiceControlItem: 0x6000003b1f80: identifier=<_UIStatusBarIdentifier: 0x600001903b60: object=_UIStatusBarVoiceControlItem>>";
    "<_UIStatusBarIdentifier: 0x600001907200: object=_UIStatusBarIndicatorCarPlayItem>" = "<_UIStatusBarIndicatorCarPlayItem: 0x6000003b1d80: identifier=<_UIStatusBarIdentifier: 0x600001907200: object=_UIStatusBarIndicatorCarPlayItem>>";
    "<_UIStatusBarIdentifier: 0x600001903be0: object=_UIStatusBarPillBackgroundActivityItem>" = "<_UIStatusBarPillBackgroundActivityItem: 0x600003c4d560: identifier=<_UIStatusBarIdentifier: 0x600001903be0: object=_UIStatusBarPillBackgroundActivityItem>>";
    "<_UIStatusBarIdentifier: 0x600001906b20: object=_UIStatusBarCellularExpandedItem>" = "<_UIStatusBarCellularExpandedItem: 0x60000366c280: identifier=<_UIStatusBarIdentifier: 0x600001906b20: object=_UIStatusBarCellularExpandedItem>>";
    "<_UIStatusBarIdentifier: 0x600001907280: object=_UIStatusBarIndicatorStudentItem>" = "<_UIStatusBarIndicatorStudentItem: 0x6000003b1dc0: identifier=<_UIStatusBarIdentifier: 0x600001907280: object=_UIStatusBarIndicatorStudentItem>>";
}

複製程式碼
(lldb) po [statusBar performSelector:@selector(containerView)]
<_UIStatusBarForegroundView: 0x7fee31804f30; frame = (0 0; 375 44); layer = <CALayer: 0x600001907720>>
複製程式碼
(lldb) po [statusBar performSelector:@selector(setForegroundColor:) withObject:[UIColor whiteColor]]
UICachedDeviceWhiteColor
複製程式碼
(lldb) po [statusBar performSelector:@selector(styleAttributes)]
<_UIStatusBarStyleAttributes: 0x60000332fd40: style=-6485417468062910266,mode=-6485417468062910314,traitCollection=<UITraitCollection: 0x600002db61c0; UserInterfaceIdiom = Phone,DisplayScale = 2,DisplayGamut = P3,HorizontalSizeClass = Compact,VerticalSizeClass = Compact,UserInterfaceStyle = Light,UserInterfaceLayoutDirection = LTR,ForceTouchCapability = Unavailable,PreferredContentSizeCategory = L,AccessibilityContrast = Normal,UserInterfaceLevel = Base>,effectiveLayoutDirection=0,iconScale=1,symbolScale=1,textColor=UIExtendedGrayColorSpace 1 1,imageTintColor=UIExtendedGrayColorSpace 1 1,imageDimmedTintColor=UIExtendedGrayColorSpace 1 0.2,imageNamePrefixes=(
    "Split_","Black_","Item_"
)>
複製程式碼

留言的朋友,把問題描述清楚了再po出來唄!

如果本文對您有用,點個? 我就滿足了~