Ios13 native end adaptation strategy (recommended)

Time:2021-9-12

With the release of IOS 13, it is necessary for the company’s projects to adapt. Cash exchange summarizes the various pits of IOS 13

1. KVC accesses private properties

The IOS 13 system upgrade has the widest impact on KVC access and modification of private properties, which directly prohibits developers from obtaining or directly setting private properties. The original intention of KVC is to allow developers to directly access and modify the attribute value of the object through the key name, which is the most typical uitextfield_ Of placeholderlabel and UISearchBar_ searchField。

Impact: app flash back under IOS 13

Error code:

//Placeholderlabel private property access
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];
//Searchfield private property access
UISearchBar *searchBar = [[UISearchBar alloc] init];
UITextField *searchTextField = [searchBar valueForKey:@"_searchField"];

Solution:

Use nsmutableattributedstring rich text to access uitextfield instead of KVC_ placeholderLabel


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

Therefore, you can create a category for uitextfeed, which is dedicated to handling the methods provided by modifying the placeholder property


#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);
}

For UISearchBar, you can traverse all its sub views to find the sub view of the specified uitextfield type, and then modify the properties through the rich text method according to the above uitextfield.

#import "UISearchBar+ChangePrivateTextFieldSubview.h"

@implementation UISearchBar (ChangePrivateTextFieldSubview)

///Modify the textfield provided by the searchBar system
- (void)changeSearchTextFieldWithCompletionBlock:(void(^)(UITextField *textField))completionBlock {

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

///Recursively traverse the subview of the UISearchBar to find the 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: for information on how to find out whether your app project uses private APIs, you can refer toIOS find private APIarticle

2. The default style of the modal pop-up viewcontroller is changed

The modal pop-up property uimodalpresentationstyle is set as a new feature of uimodalpresentationautomatic by default under IOS 13. The display style is more cool. At the same time, the modal pop-up can be closed with a drop-down gesture.

If the modal pop-up properties have been specified when the original modal pop-up viewcontroller, you can ignore this change.

If you want to continue to maintain the original default mode pop-up effect in IOS 13. It can be implemented through the method swizzling method exchange of runtime.

#import "UIViewController+ChangeDefaultPresentStyle.h"

@implementation UIViewController (ChangeDefaultPresentStyle)

+ (void)load {

 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
  Class class = [self class];
  //Replacement method
  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. Adaptation of dark mode

For the launch of dark mode, apple officially recommends that all third-party apps adapt as soon as possible. Currently, app is not forced to adapt in dark mode. Therefore, the dark mode adaptation range can now adopt the following three strategies:

  • Global turn off dark mode
  • Specifies that the page turns off dark mode
  • Global adaptation dark mode

3.1. Global turn off dark mode

Scheme 1: add a content in the project info.plist file. The key is user interface style, and the value type is set to string and light.

Scheme 2: the code forcibly turns off the dark mode and sets the current window to the light state.


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

3.2 specify the page to turn off dark mode

Starting from Xcode 11 and IOS 13, uiviewcontroller and view add the attribute overrideuserinterfacestyle. If the attribute of view object is set to the specified mode, the object and sub objects are forced to be displayed in the specified mode and will not change with the system mode.

  • Setting this property of viewcontroller will affect the view and sub view controllers of the view controller to adopt this mode
  • Setting this property will affect the view and all its child views to adopt this mode
  • Setting this property of window will affect that all contents in the window adopt this style, including the root view controller and all controllers that display contents in the window

3.3 global adaptation dark mode

Matching dark mode mainly starts from two aspects: image resource matching and color matching

Image resource adaptation

Open the image resource management library assets.xcassets, select the image material item to be adapted, open the inspectors toolbar on the far right, find the appearance option, and set it to any, dark mode. At this time, add dark appearance under item and drag the material in dark mode. The loading of dark mode picture resources is consistent with the normal loading method.

Color adaptation

IOS 13 starts uicolor to become a dynamic color. Different colors can be set in light mode and dark mode respectively. For uicolor color value management, it is stored in assets.xcassets like picture resources, and it is also adapted according to the above method. If the uicolor color value is not stored in assets.xcassets, when customizing the dynamic uicolor, two methods are added to the initialization method under 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);

These two methods require passing a block, which will return a uitraitcollection class

When the system switches between dark mode and normal mode, block callback will be triggered

Example code:


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

Of course, IOS 13 system also provides a set of basic dark mode uicolor dynamic colors by default. The specific statement is as follows:


@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);

Switching of monitoring mode

When you need to listen for system mode changes and respond, you need to use the following functions of viewcontroller

//Note: the parameter is the traitcollection before the change, and the function change needs to be rewritten
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
 
//Determine whether two uitraitcollection objects are different
- (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;

Example code:


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

System mode change, custom redraw view

When the system mode changes, the system will notify all views and viewcontrollers that the style needs to be updated, and the following methods will be triggered (refer to Apple)Official adaptation link):

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 is about to be abandoned

To set the startup diagram using launchimage, you need to provide startup diagram adaptation of various screen sizes. This method increases additional unnecessary workload with the increase of various equipment sizes. In order to solve the disadvantages brought by launchimage, IOS 8 introduces launchscreen technology. Because it supports AutoLayout + sizeclass, launchscreen can simply adapt to various screen sizes at present and in the future.

Apple has officially announced that launchscreen must be provided for all apps using IOS 13 SDK from April 2020. Creating a launchscreen is also very simple

(1) New files creates a launchscreen, creates a new image in the view under the created viewcontroller, and configures the image
(2) Adjust the image frame to occupy the full screen, and modify the image autoresizing, as shown in the figure below

5. Add a permission application for always using Bluetooth

Before IOS 13, Bluetooth can be used directly without permission prompt window, but under IOS 13, a permission application for using Bluetooth is added. You will receive the following prompt when uploading IPA package to the app store in the recent period.

Solution: you only need to add the following entries in info.plist:

<key>NSBluetoothAlwaysUsageDescription</key> 
< string > enter what to do with Bluetooth here < / String >`

6. Sign With Apple

In IOS 13 system, apple requires that the third-party login app should also support “sign with apple”. For specific practice, please refer toIOS sign with apple practice

7. Push device token adaptation

Before IOS 13, obtaining the device token is to pass the nsdata type data returned by the system through – (void) description; Method is directly converted to an nsstring string.

Get results before IOS 13:

Get results after IOS 13:

Adaptation scheme: the purpose is to convert the nsdata type data returned by the system into a string and then pass it to the push service provider- (void)description; It is used to provide relevant print information for class debugging. Strictly speaking, it should not directly obtain data from this method and apply it to the formal environment. Convert nsdata into hexstring to meet the adaptation requirements.


- (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 control changes

It mainly refers to the official UIKit modification document statement of apple.iOS 13 Release Notes

8.1. UITableView

Setting cell.contentview.backgroundcolor under IOS 13 will directly affect the selected and highlighted effects of the cell itself. It is recommended not to modify the contentview.backgroundcolor, but to set the cell itself.

8.2. UITabbar

Badge text size change

After IOS 13, the badge font changes from 13 to 17 by default. It is recommended that when initializing tabbarcontroller, the viewcontroller displaying badge calls the setbadgetextattributes: forstate: method


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

Scale scale is required to load GIF

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

//Before IOS 13
UIImage *image = [UIImage imageWithCGImage:imageRef]
//Add scale after IOS 13 (the ImageView will show the dynamic picture effect)
UIImage *image = [UIImage imageWithCGImage:imageRef scale:image.size.width / CGRectGetWidth(imageView.frame) orientation:UIImageOrientationUp];

CGImageRelease(imageRef);

Picture position adjustment without text

There is no need to adjust imageinsets under IOS 13, and the picture will be automatically displayed in the middle, so you only need to adapt to the image before IOS 13.


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

8.3. Add a diffible datasource

Under IOS 13, a set of diffible datasource API is added to uitableview and uicollectionview. In order to update the data source refresh list more efficiently, avoid the original rough refresh method – (void) reloaddata, and manually call the API controlling the refresh range of the list. It is easy to cause app crash due to nsinternalinconsistencyexception caused by inaccurate calculation.
API official link

9. New style of statusbar

A new style is added to the statusbar. The default is changed from the previous black font to automatically select to display lightcontent or darkcontent according to the system mode

For IOS 13 SDK adaptation, it will be collected and updated continuously in the future

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support developpaer.

Recommended Today

Python naming conventions

variable Generally all lowercase _ Underline segmentation Private variable_ Begin with an underscore Name without type information num_ list name = ‘jeck’ student_name = ‘cc’ for _i in range(10): pass constant uppercase _ Underline segmentation MAX_COUNT = 10 function All lowercase _ Underline segmentation Private function_ Begin with an underscore def call(): pass def call_name(): […]