With the release of iOS 13, the company’s projects will have to adapt. Here’s a round-up of the various pits in iOS 13
directory
1. KVC accesses private attributes
2. Modal popup ViewController default style changes
3. Dark mode adaptation
4. LaunchImage will soon be scrapped
5. Added the permission application for using Bluetooth all the time
6. Sign With Apple
7. Push Device Token adaptation
8. UIKit control changes
9. New styles for StatusBar
1. KVC accesses private attributes
In the latest iOS 13 update, the most widely affected is KVC access to modify private attributes, directly preventing developers from obtaining or setting private attributes directly. KVC’s original purpose is to allow developers to directly access and modify the attribute values of objects via the Key name. The most typical examples are _placeholderLabel of UITextField and _searchField of UISearchBar. Error code: In iOS 13, the App flashes back.
// placeholderLabel accesses [textField]setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"]; UISearchBar *searchBar = [[UISearchBar alloc] init]; UITextField *searchTextField = [searchBar valueForKey:@"_searchField"];
Copy the code
Solution: use NSMutableAttributedString way to replace KVC access _placeholderLabel UITextField at that time
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];
Copy the code
Therefore, you can create a Category for UITextFeild that provides methods to handle modifying placeHolder properties
#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);
}
Copy the code
For a UISearchBar, you can traverse all of its subviews, find a subview of the specified UITextField type, and modify the properties according to the above UITextField via rich text method.
#import "UISearchBar+ChangePrivateTextFieldSubview.h"@ implementation UISearchBar (ChangePrivateTextFieldSubview) / / / modify the TextField - system at SearchBar (void)changeSearchTextFieldWithCompletionBlock:(void(^)(UITextField *textField))completionBlock {if(! completionBlock) {return;
}
UITextField *textField = [self findTextFieldWithView:self];
if(textField) { completionBlock(textField); }} 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
Copy the code
PS: For more information on how to find out if your App project uses a private API, see the iOS Find Private API article
2. Modal popup ViewController default style changes
Modal pop-up properties UIModalPresentationStyle under the iOS 13 default is set to UIModalPresentationAutomatic new features, show more cool style, also can use the drop-down gestures to close the modal popup window. If the modal popover property is specified when the original mode pops out of the ViewController, you can ignore this change. You want to keep the default mode popover effect in iOS 13. This can be done by swapping the Runtime Method Swizzling Method.
#import "UIViewController+ChangeDefaultPresentStyle.h"@implementation UIViewController (ChangeDefaultPresentStyle) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; / / replace method SEL originalSelector = @ the 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]; } @endCopy the code
3. Dark mode adaptation
For the launch of dark mode, Apple officially recommended that all three apps adapt as soon as possible. There is currently no mandatory dark mode adaptation for apps. As a result, there are now three strategies for dark mode adaptation:
- Turn off dark mode globally
- Specifies that the page is off dark mode
- Global adaptation to dark mode
3.1. Turn off dark mode globally
Solution 1: Add a content in the project info.plist file. Set the Key to User Interface Style, set the value type to String and set it to Light.
Scheme 2: The code forces the dark mode off and sets the current Window state to Light.
if(@ the available (iOS 13.0. *)) {self. Window. OverrideUserInterfaceStyle = UIUserInterfaceStyleLight; }Copy the code
3.2 Turning off dark mode on a Specified page
From Xcode 13, 11, iOS UIViewController with the View of new properties overrideUserInterfaceStyle, if set the View object attribute to specify model, is forced to specify the mode displays the object and the object, Does not follow system mode changes.
- Setting the ViewController property will affect the mode of the ViewController’s views and its child view controllers
- Setting the View property affects the mode of the View and all its children
- Setting the Window property affects all content in the Window to adopt the style, including the root view controller and all controllers that display content in the Window
3.3 Global Dark Mode Adaptation
The adaptation of dark mode mainly starts from two aspects: picture resource adaptation and color adaptation
Image resource adaptation
Open assets. xcassets, select the Inspectors to be fitted, open the right side of the Inspectors toolbar, and set the Appearances to Any, Dark mode. Add a Dark Appearance under item to drag the material in Dark mode. In dark mode, image resources are loaded in the same way as normal images.
Color adaptation
In iOS 13, UIColor changes to dynamic colors. You can set different colors in Light Mode and Dark Mode. If UIColor color value management is stored in assets. xcassets like image resources, the same method is applied. In the case of the UIColor color values not stored in assets.xcassets, the initialization method in iOS 13 adds two methods to the custom dynamic UIColor
+ (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);Copy the code
- These two methods require passing a block, which returns a UITraitCollection class
- A block callback is triggered when the system switches between dark mode and normal mode:
UIColor *dynamicColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
return [UIColor whiteColor];
} else {
return[UIColor blackColor]; }}]; [self.viewsetBackgroundColor:dynamicColor];
Copy the code
Of course, iOS 13 also provides a basic set of dark mode UIColor dynamic colors by default, as described below:
@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,readonlyAPI_UNAVAILABLE(ios(13.0), TVOs (13.0)) API_UNAVAILABLE(watchos); @property (class, nonatomic,readonlyTertiaryLabelColor 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(ios) UIColor * secondarySystemBackgroundColor API_AVAILABLE (13.0)) API_UNAVAILABLE (tvos watchos); @property (class, nonatomic,readonly(ios) UIColor * tertiarySystemBackgroundColor API_AVAILABLE (13.0)) API_UNAVAILABLE (tvos watchos); @property (class, nonatomic,readonly(ios) UIColor * systemGroupedBackgroundColor API_AVAILABLE (13.0)) API_UNAVAILABLE (tvos watchos); @property (class, nonatomic,readonly(ios) UIColor * secondarySystemGroupedBackgroundColor API_AVAILABLE (13.0)) API_UNAVAILABLE (tvos watchos); @property (class, nonatomic,readonly(ios) UIColor * tertiarySystemGroupedBackgroundColor API_AVAILABLE (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, readonlyTertiarySystemFillColor API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, Watchos); @property (class, nonatomic,readonly(ios) UIColor * quaternarySystemFillColor API_AVAILABLE (13.0)) API_UNAVAILABLE (tvos watchos);Copy the code
Switch the listening mode
The following ViewController functions are used when you need to listen for and respond to changes in system mode
/ / note: traitCollection before parameters to change, change the function needs to be rewritten - (void) traitCollectionDidChange (previousTraitCollection UITraitCollection *); / / determine whether two UITraitCollection object different - hasDifferentColorAppearanceComparedToTraitCollection: (UITraitCollection (BOOL) *)traitCollection;Copy the code
Sample code:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// trait has Changed?
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
// dosomething... }}Copy the code
System mode changes, custom redraw view
When the system mode changes, the system notifies all views and ViewControllers that they need to update their styles, which triggers the following methods (see Apple’s official adaptation link) :
NSView
- (void)updateLayer;
- (void)drawRect:(NSRect)dirtyRect;
- (void)layout;
- (void)updateConstraints;
Copy the code
UIView
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)layoutSubviews;
- (void)drawRect:(NSRect)dirtyRect;
- (void)updateConstraints;
- (void)tintColorDidChange;
Copy the code
UIViewController
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)updateViewConstraints;
- (void)viewWillLayoutSubviews;
- (void)viewDidLayoutSubviews;
Copy the code
UIPresentationController
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
- (void)containerViewWillLayoutSubviews;
- (void)containerViewDidLayoutSubviews;
Copy the code
4. LaunchImage will soon be scrapped
Using LaunchImage to set up a LaunchImage requires a variety of screen sizes, which adds extra unnecessary work as device sizes increase. In order to solve the problem of LaunchImage, iOS 8 introduced LaunchScreen technology, which supports AutoLayout + SizeClass, making it easy to fit current and future screen sizes. Apple has announced that starting in April 2020, all apps using the iOS 13 SDK will be required to provide LaunchScreen. Create a LaunchScreen. Under the ViewController created, create a New Image in the View. (2) Adjust the frame of Image to fill the screen, and modify the Autoresizing of Image as shown below
5. Added the permission application for using Bluetooth all the time
Before iOS13, you can use Bluetooth directly without the permission prompt window, but in iOS13, there is a new permission request for using Bluetooth. You will receive the following prompt when you upload the IPA package to the App Store recently.
Solution: Just add the following entry to info.plist:
< key > NSBluetoothAlwaysUsageDescription < / key > < string > here input use bluetooth to do < / string > `Copy the code
6. Sign With Apple
In iOS 13, Apple requires third-party login apps to support “Sign With Apple”. For details, see the iOS Sign With Apple practice
7. Push Device Token adaptation
Before iOS 13, the Device Token is obtained by passing -(void)description to the NSData returned by the system. Method is converted directly to an NSString string. Results obtained before iOS 13:
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.2 HHX", chars[i]]];
}
return hexString;
}
Copy the code
8. UIKit control changes
Mainly refer to Apple’s official UIKit modification document declaration. iOS 13 Release Notes
8.1. UITableView
IOS Settings under 13 cell. The contentView. BackgroundColor selected and highlighted the effect will directly affect the cell itself. Not recommended for contentView. BackgroundColor modification, and to set the cell itself.
8.2. UITabbar
Badge text size changes
After iOS 13, Badge font changed from 13 to 17 by default. Advice when initializing TabbarController, display the Badge called ViewController 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];
}
Copy the code
8.2. UITabBarItem
Set the scale scale for loading GIF
NSData *data = [NSData dataWithContentsOfFile:path]; CGImageSourceRef gifSource = CGImageSourceCreateWithData(CFBridgingRetain(data), nil); size_t gifCount = CGImageSourceGetCount(gifSource); CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i,NULL); 13 before UIImage * / / iOS image = [UIImage imageWithCGImage: imageRef] / / iOS 13 after adding scale ratio (the imageView will show the effect of dynamic figure) UIImage * image = [UIImage imageWithCGImage:imageRef scale:image.size.width / CGRectGetWidth(imageView.frame) orientation:UIImageOrientationUp]; CGImageRelease(imageRef);Copy the code
Picture position adjustment without text
In iOS 13, there is no need to adjust the imageInsets, the images will be automatically centered, so you only need to adapt the images before iOS 13.
if(IOS_VERSION < 13.0) {viewController. TabBarItem. ImageInsets = UIEdgeInsetsMake (5, 0, 5, 0). }Copy the code
TabBarItem The selected color is abnormal
In iOS 13, set the color of the tabbarItem font selected state. When pushed to another ViewController and returned, the tabbarItem selected state will be the default blue color.
Set the tintColor property of the tabbar to the color originally selected.
self.tabBar.tintColor = [UIColor redColor];
Copy the code
8.3. Added Diffable DataSource
In iOS 13, we added a Diffable DataSource API for UITableView and UICollectionView. In order to update the data source refresh list more efficiently, the original rude refresh method – (void)reloadData is avoided, and the API that controls the refresh range of the list is manually called. It is easy to appear inaccurate calculation caused NSInternalInconsistencyException App crash. API official link
9. New styles for StatusBar
StatusBar has a new style. The default is not black, but lightContent or darkContent will be displayed automatically depending on the system mode
The iOS 13 SDK adaptation will be collected and updated in the future