Introduction
Tại sự kiện tháng 9 vừa rồi, cùng với sự ra mắt của bộ ba sản phẩm iPhone chủ lực, Apple đã tung ra phiên bản iOS 13 với hàng loạt update mới.
Tuy nhiên đi cùng với những tính năng hữu ích, phiên bản iOS lần này cũng được đánh giá là nhiều lỗi nhất từ trước đến nay.
Tính từ thời điểm ngày 20 tháng 9 cho đến nay, chỉ trong vòng chưa đầy 2 tháng, Apple đã phải tung ra 6 bản vá lỗi liên tục – một con số kỷ lục.
Phiên bản iOS 13 ngoài việc nhiều lỗi gây phiền toái cho người dùng thì đối với các developer thì cũng đau đầu không kém. Khi mà có rất nhiều thay đổi nhỏ về UI mà Apple không có document cụ thể, dẫn đến hàng loạt bug khó fix.
Vì vậy trong bài viết này, chúng ta sẽ cùng chia sẻ và tìm hiểu một số thay đổi chú ý khi update UI trên iOS 13.
Adaptation Requirement
Starting April, 2020, all iPhone and iPad apps submitted to the App Store will need to be built with the iOS 13 SDK or later. They must also support the all-screen design of iPhone XS Max or the 12.9-inch iPad Pro (3rd Generation), or later.
Theo như trang Apple developer công bố, kể từ tháng 4 năm 2020, tất cả các game và app cho iPhone và iPad submit lên App Store đều phải được compile bằng bộ SDK của iOS 13 hoặc cao hơn (nghĩa là build bằng Xcode 11.0 trở lên).
Và tất cả phải support design full screen cho các loại màn hình mới từ iPhone XS Max hoặc iPad Pro 12.9 inch (thế hệ thứ 3) hoặc cao hơn.
Những app không đủ điều kiện sẽ bị reject submission hoặc tệ hơn có thể bị xóa khỏi App Store.
New Feature Adaptation
Dark Mode
iOS 13 giới thiệu chế độ Dark Mode mới bằng việc UIKit đã support các API, các system colors và adaptive images trong xcasset
.
Tìm hiểu kỹ hơn về Dark Mode tại video WWDC 2019: Implementing Dark Mode on iOS
Nếu bạn thấy chưa sẵn sàng để implement Dark Mode thì có thể disable nó bằng 2 cách đơn giản sau:
- Trong Info.plist, thêm key
UIUserInterfaceStyle
(Interface Style) và set giá trị này làLight
thì app của bạn sẽ luôn hiển thị theme sáng mặc dù đang bật chế độ Dark Mode. - Dùng code, set property
overrideUserInterfaceStyle
thành.light
ở bất kỳ UIView hay UIViewController nào mà bạn muốn.
Tuy nhiên các popup alert view mặc định của hệ thống, ví dụ như alert xin quyền notification, xin quyền camera… vẫn hiển thị màu đen ngay cả khi Dark Mode đang off.
Sign In with Apple
Kể từ iOS 13, Apple chính thức ra mắt một cách đăng nhập nhanh chóng và tiện lợi vào các app và website: Sign In with Apple.
Đây là một feature mới, vì vậy nó chỉ có thể implement trên Xcode 11. Theo như App Store App Review Guide mới nhất thì:
Apps that exclusively use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.
Nếu app của bạn có sử dụng các login service của các bên thứ 3 (như Facebook Login, Google Sign-in, Twitter, LinkedIn, Amazon, Zalo…) để xác thực tài khoản user thì bạn BẮT BUỘC phải implement thêm cả option Sign In with Apple.
Các trường hợp sau thì không bắt buộc:
- Những app sử dụng các tài khoản internal của một công ty, tổ chức để register, login. (Ví dụ: app WSM của Sun*).
- Những app giáo dục, enterprise, bussiness mà xác thực user sử dụng các tài khoản giáo dục, enterprise đã tồn tại trước đó. (Ví dụ: app Edumall).
- Những app được hỗ trợ bởi chính phủ, tổ chức xác minh thông qua các hệ thống quản lý công dân, căn cước, ID điện tử… (Ví dụ: các app quản lý dân sự).
- Những app là client cho dịch vụ của một bên thứ 3 cụ thể, yêu cầu user đăng nhập để có truy cập, sử dụng content, media của bên thứ 3 đó. (Ví dụ: app Netflix).
Cùng với sự xuất hiện của Sign In with Apple, Apple có đề cập trong mục News and Updates rằng:
Starting today, new apps submitted to the App Store can follow these guidelines. Existing apps and app updates must follow them by April 2020.
Bắt đầu từ ngày 12 tháng 9 năm 2019, các app mới submit để review lên App Store phải tuân thủ các tiêu chuẩn trên, được quy định trong App Review Guide.
Những app hiện tại và các update phải được hoàn thiện vào hạn chót vào tháng 4 năm 2020.
API Adaption
Private method KVC
Trong iOS 13, khi dùng các method KVC (Key Value Coding) như valueForKey
hoặc setValue:forKey
để get hoặc set các private property có thể gây ra crash runtime với log như sau:
1 2 3 4 | *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to UISearchBar's _searchField ivar is prohibited. This is an application bug' |
Ở ví dụ trên, app đã bị crash khi chúng ta cố get private property _searchField
của một UISearchBar.
1 2 | UITextField <span class="token operator">*</span>textField <span class="token operator">=</span> <span class="token punctuation">[</span>searchBar valueForKey<span class="token punctuation">:</span><span class="token string">@"_searchField"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> |
Để giải quyết vấn đề trên, có thể sử dụng public property mới searchTextField
:
1 2 | searchBar<span class="token punctuation">.</span>searchTextField<span class="token punctuation">.</span>placeholder <span class="token operator">=</span> <span class="token string">@"Search comics"</span><span class="token punctuation">;</span> |
hoặc viết method tìm view và sử dụng:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token operator">-</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span><span class="token punctuation">)</span>findViewWithClassName<span class="token punctuation">:</span><span class="token punctuation">(</span>NSString <span class="token operator">*</span><span class="token punctuation">)</span>className inView<span class="token punctuation">:</span><span class="token punctuation">(</span>UIView <span class="token operator">*</span><span class="token punctuation">)</span>view <span class="token punctuation">{</span> Class specificView <span class="token operator">=</span> <span class="token function">NSClassFromString</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span>view isKindOfClass<span class="token punctuation">:</span>specificView<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> view<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>view<span class="token punctuation">.</span>subviews<span class="token punctuation">.</span>count <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span>subView <span class="token keyword">in</span> view<span class="token punctuation">.</span>subviews<span class="token punctuation">)</span> <span class="token punctuation">{</span> UIView <span class="token operator">*</span>targetView <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token keyword">self</span> findViewWithClassName<span class="token punctuation">:</span>className inView<span class="token punctuation">:</span>subView<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>targetView <span class="token operator">!=</span> nil<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> targetView<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> nil<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Sử dụng</span> UITextField <span class="token operator">*</span>textField <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token keyword">self</span> findViewWithClassName<span class="token punctuation">:</span><span class="token string">@"UITextField"</span> inView<span class="token punctuation">:</span>_searchBar<span class="token punctuation">]</span> |
Một số tình huống bị crash cụ thể và cách fix sử dụng method findViewWithClassName
trên:
1 2 3 4 5 6 7 | <span class="token comment">// Crash</span> <span class="token punctuation">[</span>searchBar setValue<span class="token punctuation">:</span><span class="token string">@"Cancel"</span> forKey<span class="token punctuation">:</span><span class="token string">@"_cancelButtonText"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Cách fix</span> UIButton <span class="token operator">*</span>cancelButton <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token keyword">self</span> findViewWithClassName<span class="token punctuation">:</span><span class="token function">NSStringFromClass</span><span class="token punctuation">(</span><span class="token punctuation">[</span>UIButton class<span class="token punctuation">]</span><span class="token punctuation">)</span> inView<span class="token punctuation">:</span>searchBar<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">[</span>cancelButton setTitle<span class="token punctuation">:</span><span class="token string">@"Cancel"</span> forState<span class="token punctuation">:</span>UIControlStateNormal<span class="token punctuation">]</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">// Crash, getValue _placeholderLabel không crash nhưng setValue thì sẽ gây crash</span> <span class="token punctuation">[</span>textField setValue<span class="token punctuation">:</span><span class="token punctuation">[</span>UIColor blueColor<span class="token punctuation">]</span> forKeyPath<span class="token punctuation">:</span><span class="token string">@"_placeholderLabel.textColor"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">[</span>textField setValue<span class="token punctuation">:</span><span class="token punctuation">[</span>UIFont systemFontOfSize<span class="token punctuation">:</span><span class="token number">20</span><span class="token punctuation">]</span> forKeyPath<span class="token punctuation">:</span><span class="token string">@"_placeholderLabel.font"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Cách fix 1: thay vì sử dụng _placeholderLabel thì hãy dùng placeholderLabel</span> <span class="token punctuation">[</span>textField setValue<span class="token punctuation">:</span><span class="token punctuation">[</span>UIColor blueColor<span class="token punctuation">]</span> forKeyPath<span class="token punctuation">:</span><span class="token string">@"placeholderLabel.textColor"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">[</span>textField setValue<span class="token punctuation">:</span><span class="token punctuation">[</span>UIFont systemFontOfSize<span class="token punctuation">:</span><span class="token number">20</span><span class="token punctuation">]</span> forKeyPath<span class="token punctuation">:</span><span class="token string">@"placeholderLabel.font"</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Cách fix 2: dùng NSAttributedString</span> textField<span class="token punctuation">.</span>attributedPlaceholder <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">[</span>NSAttributedString alloc<span class="token punctuation">]</span> initWithString<span class="token punctuation">:</span><span class="token string">@"Enter text"</span> attributes<span class="token punctuation">:</span><span class="token operator">@</span><span class="token punctuation">{</span> NSForegroundColorAttributeName<span class="token punctuation">:</span> <span class="token punctuation">[</span>UIColor blueColor<span class="token punctuation">]</span><span class="token punctuation">,</span> NSFontAttributeName<span class="token punctuation">:</span> <span class="token punctuation">[</span>UIFont systemFontOfSize<span class="token punctuation">:</span><span class="token number">20</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">;</span> |
Device Token
Khi user cung cấp quyền push notification, chúng ta sẽ nhận được device token định danh cho device đó và dùng nó để gửi lên server.
Bình thường, ta chỉ cần convert device token từ dạng NSData
sang kiểu NSString
và bỏ đi các ký tự thừa bằng code.
1 2 3 4 5 6 7 8 | <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span>application<span class="token punctuation">:</span><span class="token punctuation">(</span>UIApplication <span class="token operator">*</span><span class="token punctuation">)</span>application didRegisterForRemoteNotificationsWithDeviceToken<span class="token punctuation">:</span><span class="token punctuation">(</span>NSData <span class="token operator">*</span><span class="token punctuation">)</span>deviceToken <span class="token punctuation">{</span> NSString <span class="token operator">*</span>token <span class="token operator">=</span> <span class="token punctuation">[</span>deviceToken description<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>NSString <span class="token operator">*</span>symbol <span class="token keyword">in</span> <span class="token operator">@</span><span class="token punctuation">[</span><span class="token string">@" "</span><span class="token punctuation">,</span> <span class="token string">@"<"</span><span class="token punctuation">,</span> <span class="token string">@">"</span><span class="token punctuation">,</span> <span class="token string">@"-"</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> token <span class="token operator">=</span> <span class="token punctuation">[</span>token stringByReplacingOccurrencesOfString<span class="token punctuation">:</span>symbol withString<span class="token punctuation">:</span><span class="token string">@""</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">NSLog</span><span class="token punctuation">(</span><span class="token string">@"deviceToken:%@"</span><span class="token punctuation">,</span> token<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Tuy nhiên, trong iOS 13, đoạn code trên sẽ bị lỗi, không cho kết quả như mong muốn:
1 2 | {length = 32, bytes = 0xd7f9fe34 69be14d1 fa51be22 329ac80d ... 5ad13017 b8ad0736 } |
Cách parse đối với iOS 13 như sau:
1 2 3 4 5 6 7 8 9 10 | <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span>application<span class="token punctuation">:</span><span class="token punctuation">(</span>UIApplication <span class="token operator">*</span><span class="token punctuation">)</span>application didRegisterForRemoteNotificationsWithDeviceToken<span class="token punctuation">:</span><span class="token punctuation">(</span>NSData <span class="token operator">*</span><span class="token punctuation">)</span>deviceToken <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">[</span>deviceToken isKindOfClass<span class="token punctuation">:</span><span class="token punctuation">[</span>NSData class<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token keyword">unsigned</span> <span class="token operator">*</span>tokenBytes <span class="token operator">=</span> <span class="token punctuation">[</span>deviceToken bytes<span class="token punctuation">]</span><span class="token punctuation">;</span> NSString <span class="token operator">*</span>hexToken <span class="token operator">=</span> <span class="token punctuation">[</span>NSString stringWithFormat<span class="token punctuation">:</span><span class="token string">@"%08x%08x%08x%08x%08x%08x%08x%08x"</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">6</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ntohl</span><span class="token punctuation">(</span>tokenBytes<span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token function">NSLog</span><span class="token punctuation">(</span><span class="token string">@"deviceToken:%@"</span><span class="token punctuation">,</span> hexToken<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Modal Pop-up Default Style
Kể từ iOS 13, khi sử dụng method presentViewController:animated:completion:
để present một view controller thì sẽ có animation và behavior mới như sau:
Đó là bởi vì giá trị mặc định của property modalPresentationStyle
của UIViewController
đã bị thay đổi. Enum UIModalPresentationStyle
có thêm một value mới là UIModalPresentationAutomatic
. Giá trị này với hầu hết với các view controller được set bằng UIModalPresentationPageSheet
.
Lưu ý rằng, với kiểu hiển thị cũ view controller mới full screen khi present thì presenting view controller sẽ call các method viewWillDisappear
và viewDidDisappear
. Khi dismiss presenting view controller thì các method viewWillAppear
và viewDidAppear
sẽ được call. Tuy nhiên với kểu present page sheet mới này thì các method trên sẽ không được call.
Solution: Set lại thủ công giá trị property này thành UIModalPresentationFullScreen
.
1 2 3 4 | <span class="token operator">-</span> <span class="token punctuation">(</span>UIModalPresentationStyle<span class="token punctuation">)</span>modalPresentationStyle <span class="token punctuation">{</span> <span class="token keyword">return</span> UIModalPresentationFullScreen<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
UISearchBar Background
Để xóa bỏ phần background đen của UISearchBar
, chúng ta thường dùng cách duyệt qua view structure của search bar và remove nó khỏi superview.
1 2 3 4 5 6 7 | <span class="token keyword">for</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span>view <span class="token keyword">in</span> _searchBar<span class="token punctuation">.</span>subviews<span class="token punctuation">.</span>lastObject<span class="token punctuation">.</span>subviews<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span>view isKindOfClass<span class="token punctuation">:</span><span class="token function">NSClassFromString</span><span class="token punctuation">(</span><span class="token string">@"UISearchBarBackground"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span>view removeFromSuperview<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Tuy nhiên khi sử dụng cách này trên iOS 13 sẽ gây ra crash:
1 2 | *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Missing or detached view for search bar layout' |
Solution: set layer.contents
của UISearchBarBackground
bằng nil:
1 2 3 4 5 6 7 | <span class="token keyword">for</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span>view <span class="token keyword">in</span> _searchBar<span class="token punctuation">.</span>subviews<span class="token punctuation">.</span>lastObject<span class="token punctuation">.</span>subviews<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span>view isKindOfClass<span class="token punctuation">:</span><span class="token function">NSClassFromString</span><span class="token punctuation">(</span><span class="token string">@"UISearchBarBackground"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> view<span class="token punctuation">.</span>layer<span class="token punctuation">.</span>contents <span class="token operator">=</span> nil<span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
UITabBarButton Structure
View structure của UITabBarButton
và nhiều control mặc định trong iOS 13 đã bị thay đổi:
Như chúng ta có thể thấy UITabBarSwappableImageView
và UITabBarButtonLabel
đã được wrap vào một view mới kiểu _UIVisualEffectContentView
. Và khi custom tab bar button, chúng ta thường duyệt qua các subview để tìm UITabBarSwappableImageView
và thay đổi.
Nhưng vì nó đã được wrap trong view khác nên sẽ cần chú ý nếu muốn set frame, customize…
UINavigationBar Button Margins
Bắt đầu từ iOS 11 thì UINavigationBar
sử dụng auto layout với các button trái, phải cách mép 16 pts (đối với các device có width <= 375 pts) hoặc 20 pts (đối với các device có width > 375 pts).
Để tăng kích thước vùng tap action của button, chúng ta thường định nghĩa một subclass của UINavigationBar
và override method layoutSubviews
. Sau đó duyệt qua các subview, tìm _UINavigationBarContentView
và set layoutMargins
bằng UIEdgeInsetsZero
.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span>layoutSubviews <span class="token punctuation">{</span> <span class="token punctuation">[</span><span class="token keyword">super</span> layoutSubviews<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span>subview <span class="token keyword">in</span> <span class="token keyword">self</span><span class="token punctuation">.</span>subviews<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token function">NSStringFromClass</span><span class="token punctuation">(</span><span class="token punctuation">[</span>subview class<span class="token punctuation">]</span><span class="token punctuation">)</span> containsString<span class="token punctuation">:</span><span class="token string">@"_UINavigationBarContentView"</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> subview<span class="token punctuation">.</span>layoutMargins <span class="token operator">=</span> UIEdgeInsetsZero<span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Cách này cũng sẽ lại gây crash trên iOS 13, error message:
1 2 | *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Client error attempting to change layout margins of a private view' |
Solution: sử dụng frame để mở rộng các chiều kích thước:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span>layoutSubviews <span class="token punctuation">{</span> <span class="token punctuation">[</span><span class="token keyword">super</span> layoutSubviews<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>UIView <span class="token operator">*</span>subview <span class="token keyword">in</span> <span class="token keyword">self</span><span class="token punctuation">.</span>subviews<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token function">NSStringFromClass</span><span class="token punctuation">(</span><span class="token punctuation">[</span>subview class<span class="token punctuation">]</span><span class="token punctuation">)</span> containsString<span class="token punctuation">:</span><span class="token string">@"_UINavigationBarContentView"</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">[</span>UIDevice currentDevice<span class="token punctuation">]</span><span class="token punctuation">.</span>systemVersion<span class="token punctuation">.</span>floatValue <span class="token operator">>=</span> <span class="token number">13.0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> UIEdgeInsets margins <span class="token operator">=</span> subview<span class="token punctuation">.</span>layoutMargins<span class="token punctuation">;</span> subview<span class="token punctuation">.</span>frame <span class="token operator">=</span> <span class="token function">CGRectMake</span><span class="token punctuation">(</span><span class="token operator">-</span>margins<span class="token punctuation">.</span>left<span class="token punctuation">,</span> <span class="token operator">-</span>margins<span class="token punctuation">.</span>top<span class="token punctuation">,</span> margins<span class="token punctuation">.</span>left <span class="token operator">+</span> margins<span class="token punctuation">.</span>right <span class="token operator">+</span> subview<span class="token punctuation">.</span>frame<span class="token punctuation">.</span>size<span class="token punctuation">.</span>width<span class="token punctuation">,</span> margins<span class="token punctuation">.</span>top <span class="token operator">+</span> margins<span class="token punctuation">.</span>bottom <span class="token operator">+</span> subview<span class="token punctuation">.</span>frame<span class="token punctuation">.</span>size<span class="token punctuation">.</span>height<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> subview<span class="token punctuation">.</span>layoutMargins <span class="token operator">=</span> UIEdgeInsetsZero<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Còn rất nhiều bug, thay đổi khác trên iOS 13, chúng ta sẽ tìm hiểu trong bài viết lần sau.
Source article: https://juejin.im/post/5d8af88ef265da5b6e0a23ac