本文轉(zhuǎn)載自:https://juejin.im/post/5d85c3fde51d453b7779d604
本文對應(yīng)github地址iOS 13 問題解決以及蘋果登錄,如果由于github調(diào)整導(dǎo)致資源找不到或細(xì)節(jié)更改,請訪問github
本文直接搬磚,隨便看看就行
iOS 13 (Xcode11編譯時)問題解決以及蘋果登錄
-
KVC修改私有屬性可能Crash(不是所有,不是所有,不是所有),需要用別的姿勢替代。
-
UITextField的私有屬性_placeholderLabel的字體顏色,
如
[textField setValue:color forKeyPath:@"_placeholderLabel.textColor"];
會crash。那么該怎么辦呢?下面提供幾種姿勢
姿勢一:采用富文本形式
_textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : color}]; 復(fù)制代碼
姿勢二:new方式創(chuàng)建一個新label(太low不建議用)
// 必須new創(chuàng)建,如果alloc-init創(chuàng)建還是crash(兩種方式差別自行g(shù)oogle,不是BD) UILabel * placeholderLabel = [UILabel new]; placeholderLabel.text = @"666"; placeholderLabel.textColor = [UIColor blueColor]; [_textField setValue: placeholderLabel forKey:@"_placeholderLabel"];//new創(chuàng)建這里并沒有crash 復(fù)制代碼
姿勢三:Runtime
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel"); UILabel *placeholderLabel = object_getIvar(_textField, ivar); placeholderLabel.textColor = color; 復(fù)制代碼
[searchBar valueForKey:@"_searchField"]; 取值崩潰
- (UITextField *)ddy_SearchField { #ifdef __IPHONE_13_0 if (@available(iOS 13.0, *)) { return self.searchTextField; } #endif return [self valueForKey:@"_searchField"]; } 復(fù)制代碼
所以修改UISearchBar占位字符可以把上面的結(jié)合使用
-
-
模態(tài)彈出時 modalPresentationStyle 改變了默認(rèn)值
- 在iOS13之前的版本中, UIViewController的UIModalPresentationStyle屬性默認(rèn)是UIModalPresentationFullScreen,而在iOS13中變成了UIModalPresentationPageSheet。
- 我們需要在presentViewController時,設(shè)置一下UIModalPresentationStyle,就可以達(dá)到舊的效果。
- 如果PageSheet想控制下拉dismiss,modalInPresentation可以控制
UIViewController+DDYPresent.h下載該文件
/// 一個一個改浪費時間,適合版本迭代中逐步替換; /// 直接重寫-modalPresentationStyle 侵入性太大,造成系統(tǒng)彈出也被重置,或者某個控制器想改變樣式都不能,不太友好 /// 所以用一個類方法控制全局,一個實例方法控制具體某個控制器實例樣式。 #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface UIViewController (DDYPresent) /// 如果以后迭代版本想全部用系統(tǒng)原來樣式,統(tǒng)一返回NO即可 /// rentrn BOOL UIImagePickerController/UIAlertController is NO,others is YES + (BOOL)ddy_GlobalAutoSetModalPresentationStyle; /// 具體某個控制器不想更改了(想用系統(tǒng)默認(rèn))設(shè)置NO /// return BOOL [Class ddy_GlobalAutoSetModalPresentationStyle]; @property (nonatomic, assign) BOOL ddy_AutoSetModalPresentationStyle; @end NS_ASSUME_NONNULL_END 復(fù)制代碼
UIViewController+DDYPresent.m下載該文件
#import "UIViewController+DDYPresent.h" #import <objc/runtime.h> #import <StoreKit/StoreKit.h> 復(fù)制代碼
import <SafariServices/SafariServices.h>
@implementation UIViewController (DDYPresent)
+ (void)changeOriginalSEL:(SEL)orignalSEL swizzledSEL:(SEL)swizzledSEL {
Method originalMethod = class_getInstanceMethod([self class], orignalSEL);
Method swizzledMethod = class_getInstanceMethod([self class], swizzledSEL);
if (class_addMethod([self class], orignalSEL, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod([self class], swizzledSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSEL = @selector(presentViewController:animated:completion:);
SEL swizzledSEL = @selector(ddy_PresentViewController:animated:completion:);
[self changeOriginalSEL:originalSEL swizzledSEL:swizzledSEL];
});
}
- (void)ddy_PresentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
if (@available(iOS 13.0, *)) {
if (viewControllerToPresent.ddy_AutoSetModalPresentationStyle) {
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
}
[self ddy_PresentViewController:viewControllerToPresent animated:flag completion:completion];
}
- (void)setDdy_AutoSetModalPresentationStyle:(BOOL)ddy_AutoSetModalPresentationStyle {
objc_setAssociatedObject(self, @selector(ddy_AutoSetModalPresentationStyle), @(ddy_AutoSetModalPresentationStyle), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)ddy_AutoSetModalPresentationStyle {
NSNumber *obj = objc_getAssociatedObject(self, @selector(ddy_AutoSetModalPresentationStyle));
return obj ? [obj boolValue] : [self.class ddy_GlobalAutoSetModalPresentationStyle];
}
// MARK: 排除一些系統(tǒng)控制器
+ (BOOL)ddy_GlobalAutoSetModalPresentationStyle {
if ([self isKindOfClass:[UIImagePickerController class]]) {
return NO;
} else if ([self isKindOfClass:[UIAlertController class]]) {
return NO;
} else if ([self isKindOfClass:[UIActivityViewController class]]) {
return NO;
} else if ([self isKindOfClass:[UIDocumentInteractionController class]]) {
return NO;
} else if ([self isKindOfClass:[SFSafariViewController class]]) {
return NO;
}
#ifdef __IPHONE_10_3
else if ([self isKindOfClass:[SKStoreReviewController class]]) {
return NO;
} else if ([self isKindOfClass:[SKStoreProductViewController class]]) {
return NO;
}
#endif
return YES;
}
@end
獲取DeviceToken姿勢改變
##### iOS13之前
```
NSString *myToken = [deviceToken description];
myToken = [myToken stringByReplacingOccurrencesOfString: @"<" withString: @""];
myToken = [myToken stringByReplacingOccurrencesOfString: @">" withString: @""];
myToken = [myToken stringByReplacingOccurrencesOfString: @" " withString: @""];
```
##### iOS13之后(不建議這樣寫)
[為什么不建議這樣寫](https://link.juejin.im/?target=https%3A%2F%2Fdeveloper.apple.com%2Fdocumentation%2Fuikit%2Fuiapplicationdelegate%2F1622958-application%3Flanguage%3Dobjc)APNs device tokens are of variable length. Do not hard-code their size
```
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
if (![deviceToken isKindOfClass:[NSData class]]) return;
const unsigned *tokenBytes = [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);
}
##### 推薦的寫法
```
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
if (!deviceToken || ![deviceToken isKindOfClass:[NSData class]] || deviceToken.length==0) {
return;
}
NSString *(^getDeviceToken)(void) = ^() {
if (@available(iOS 13.0, *)) {
const unsigned char *dataBuffer = (const unsigned char *)deviceToken.bytes;
NSMutableString *myToken = [NSMutableString stringWithCapacity:(deviceToken.length * 2)];
for (int i = 0; i < deviceToken.length; i++) {
[myToken appendFormat:@"%02x", dataBuffer[I]];
}
return (NSString *)[myToken copy];
} else {
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"<>"];
NSString *myToken = [[deviceToken description] stringByTrimmingCharactersInSet:characterSet];
return [myToken stringByReplacingOccurrencesOfString:@" " withString:@""];
}
};
NSString *myToken = getDeviceToken();
NSLog(@"%@", myToken);
}
```
-
UIWebView
- 蘋果已經(jīng)從iOS13禁止UIWebView方式了,需要更換WKWebView(過渡期仍可用,只是郵件警告,目前不影響審核)
-
即將廢棄的 LaunchImage
- 從iOS 8 蘋果引入了 LaunchScreen,我們可以設(shè)置LaunchScreen來作為啟動頁。當(dāng)然,現(xiàn)在你還可以使用LaunchImage來設(shè)置啟動圖。不過使用LaunchImage的話,要求我們必須提供各種屏幕尺寸的啟動圖,來適配各種設(shè)備,隨著蘋果設(shè)備尺寸越來越多,這種方式顯然不夠 Flexible。而使用 LaunchScreen的話,情況會變的很簡單, LaunchScreen是支持AutoLayout+SizeClass的,所以適配各種屏幕都不在話下。
- 從2020年4月開始,所有使? iOS13 SDK的 App將必須提供 LaunchScreen,LaunchImage即將退出歷史舞臺。
-
MPMoviePlayerController 被禁止
- 這個用的人應(yīng)該不多了,如果是則更換姿勢,如用AVPlayer
-
UITableViewCell中cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;方框問題
- 低版本Xcode(如Xcode10)編譯運行在iOS13上則會出現(xiàn)方框,如果用Xcode11編譯則不會出現(xiàn)
-
增加蘋果登錄(可選)
去開發(fā)者網(wǎng)站在 Sign in with Apple 開啟功能
Xcode 里面 Signing & Capabilities 開啟 Sign in with Apple 功能
-
利用 ASAuthorizationAppleIDButton
ASAuthorizationAppleIDButton *button = [ASAuthorizationAppleIDButton buttonWithType:ASAuthorizationAppleIDButtonTypeSignIn style:ASAuthorizationAppleIDButtonStyleWhite]; [button addTarget:self action:@selector(signInWithApple) forControlEvents:UIControlEventTouchUpInside]; button.center = self.view.center; button.bounds = CGRectMake(0, 0, 40, 40); // 寬度過小就沒有文字了,只剩圖標(biāo) [self.view addSubview:button];
-
ASAuthorizationControllerPresentationContextProviding
- ASAuthorizationControllerPresentationContextProviding 就一個方法,主要是告訴 ASAuthorizationController 展示在哪個 window 上
#pragma mark - ASAuthorizationControllerPresentationContextProviding - (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)) { return self.view.window; }
-
Authorization 發(fā)起授權(quán)登錄請求
- (void)signInWithApple API_AVAILABLE(ios(13.0)) { ASAuthorizationAppleIDProvider *provider = [[ASAuthorizationAppleIDProvider alloc] init]; ASAuthorizationAppleIDRequest *request = [provider createRequest]; request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail]; ASAuthorizationController *vc = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]]; vc.delegate = self; vc.presentationContextProvider = self; [vc performRequests]; }
- ASAuthorizationAppleIDProvider 這個類比較簡單,頭文件中可以看出,主要用于創(chuàng)建一個 ASAuthorizationAppleIDRequest 以及獲取對應(yīng) userID 的用戶授權(quán)狀態(tài)。在上面的方法中我們主要是用于創(chuàng)建一個 ASAuthorizationAppleIDRequest ,用戶授權(quán)狀態(tài)的獲取后面會提到。
- 給創(chuàng)建的 request 設(shè)置 requestedScopes ,這是個 ASAuthorizationScope 數(shù)組,目前只有兩個值,ASAuthorizationScopeFullName 和 ASAuthorizationScopeEmail ,根據(jù)需求去設(shè)置即可。
- 然后,創(chuàng)建 ASAuthorizationController ,它是管理授權(quán)請求的控制器,給其設(shè)置 delegate 和 presentationContextProvider ,最后啟動授權(quán) performRequests
-
授權(quán)回調(diào)處理
#pragma mark - ASAuthorizationControllerDelegate - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) { if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { ASAuthorizationAppleIDCredential *credential = authorization.credential; NSString *state = credential.state; NSString *userID = credential.user; NSPersonNameComponents *fullName = credential.fullName; NSString *email = credential.email; NSString *authorizationCode = [[NSString alloc] initWithData:credential.authorizationCode encoding:NSUTF8StringEncoding]; // refresh token NSString *identityToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding]; // access token ASUserDetectionStatus realUserStatus = credential.realUserStatus; NSLog(@"state: %@", state); NSLog(@"userID: %@", userID); NSLog(@"fullName: %@", fullName); NSLog(@"email: %@", email); NSLog(@"authorizationCode: %@", authorizationCode); NSLog(@"identityToken: %@", identityToken); NSLog(@"realUserStatus: %@", @(realUserStatus)); } } - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) { NSString *errorMsg = nil; switch (error.code) { case ASAuthorizationErrorCanceled: errorMsg = @"用戶取消了授權(quán)請求"; break; case ASAuthorizationErrorFailed: errorMsg = @"授權(quán)請求失敗"; break; case ASAuthorizationErrorInvalidResponse: errorMsg = @"授權(quán)請求響應(yīng)無效"; break; case ASAuthorizationErrorNotHandled: errorMsg = @"未能處理授權(quán)請求"; break; case ASAuthorizationErrorUnknown: errorMsg = @"授權(quán)請求失敗未知原因"; break; } NSLog(@"%@", errorMsg); }
User ID: Unique, stable, team-scoped user ID,蘋果用戶唯一標(biāo)識符,該值在同一個開發(fā)者賬號下的所有 App 下是一樣的,開發(fā)者可以用該唯一標(biāo)識符與自己后臺系統(tǒng)的賬號體系綁定起來。
Verification data: Identity token, code,驗證數(shù)據(jù),用于傳給開發(fā)者后臺服務(wù)器,然后開發(fā)者服務(wù)器再向蘋果的身份驗證服務(wù)端驗證本次授權(quán)登錄請求數(shù)據(jù)的有效性和真實性,詳見 Sign In with Apple REST API。如果驗證成功,可以根據(jù) userIdentifier 判斷賬號是否已存在,若存在,則返回自己賬號系統(tǒng)的登錄態(tài),若不存在,則創(chuàng)建一個新的賬號,并返回對應(yīng)的登錄態(tài)給 App。
Account information: Name, verified email,蘋果用戶信息,包括全名、郵箱等。
Real user indicator: High confidence indicator that likely real user,用于判斷當(dāng)前登錄的蘋果賬號是否是一個真實用戶,取值有:unsupported、unknown、likelyReal。
失敗情況會走 authorizationController:didCompleteWithError: 這個方法,具體看代碼吧
-
其他情況的處理
- 用戶終止 App 中使用 Sign in with Apple 功能
- 用戶在設(shè)置里注銷了 AppleId
這些情況下,App 需要獲取到這些狀態(tài),然后做退出登錄操作,或者重新登錄。
我們需要在 App 啟動的時候,通過 getCredentialState:completion: 來獲取當(dāng)前用戶的授權(quán)狀態(tài)- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if (@available(iOS 13.0, *)) { NSString *userIdentifier = 鑰匙串中取出的 userIdentifier; if (userIdentifier) { ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new]; [appleIDProvider getCredentialStateForUserID:userIdentifier completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) { switch (credentialState) { case ASAuthorizationAppleIDProviderCredentialAuthorized: // The Apple ID credential is valid break; case ASAuthorizationAppleIDProviderCredentialRevoked: // Apple ID Credential revoked, handle unlink break; case ASAuthorizationAppleIDProviderCredentialNotFound: // Credential not found, show login UI break; } }]; } } return YES; }
ASAuthorizationAppleIDProviderCredentialState 解析如下:
- ASAuthorizationAppleIDProviderCredentialAuthorized 授權(quán)狀態(tài)有效;
- ASAuthorizationAppleIDProviderCredentialRevoked 上次使用蘋果賬號登錄的憑據(jù)已被移除,需解除綁定并重新引導(dǎo)用戶使用蘋果登錄;
- ASAuthorizationAppleIDProviderCredentialNotFound 未登錄授權(quán),直接彈出登錄頁面,引導(dǎo)用戶登錄
-
還可以通過通知方法來監(jiān)聽 revoked 狀態(tài),可以添加 ASAuthorizationAppleIDProviderCredentialRevokedNotification 這個通知
- (void)observeAppleSignInState { if (@available(iOS 13.0, *)) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSignInWithAppleStateChanged:) name:ASAuthorizationAppleIDProviderCredentialRevokedNotification object:nil]; } } - (void)handleSignInWithAppleStateChanged:(NSNotification *)notification { // Sign the user out, optionally guide them to sign in again NSLog(@"%@", notification.userInfo); }
-
One more thing
蘋果還把 iCloud KeyChain password 集成到了這套 API 里,我們在使用的時候,只需要在創(chuàng)建 request 的時候,多創(chuàng)建一個 ASAuthorizationPasswordRequest ,這樣如果 KeyChain 里面也有登錄信息的話,可以直接使用里面保存的用戶名和密碼進(jìn)行登錄。代碼如下
- (void)perfomExistingAccountSetupFlows API_AVAILABLE(ios(13.0)) { ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new]; ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIDProvider createRequest]; ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest]; NSMutableArray <ASAuthorizationRequest *>* array = [NSMutableArray arrayWithCapacity:2]; if (authAppleIDRequest) { [array addObject:authAppleIDRequest]; } if (passwordRequest) { [array addObject:passwordRequest]; } NSArray <ASAuthorizationRequest *>* requests = [array copy]; ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:requests]; authorizationController.delegate = self; authorizationController.presentationContextProvider = self; [authorizationController performRequests]; } #pragma mark - ASAuthorizationControllerDelegate - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) { if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) { ASPasswordCredential *passwordCredential = authorization.credential; NSString *userIdentifier = passwordCredential.user; NSString *password = passwordCredential.password; NSLog(@"userIdentifier: %@", userIdentifier); NSLog(@"password: %@", password); } }
-
Flutter1.9.1+hotfix2 Dart2.5 在iOS13真機上啟動不了
錯誤信息 Device doesn't support wireless sync. AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL)
解決方案
更新Flutter到dev或master
flutter channel dev // 或下面 // flutter channel master // 然后執(zhí)行 flutter doctor // dart2.6 flutter1.10
-
獲取不到wifiSSID(wifi名)
Dear Developer, As we announced at WWDC19, we're making changes to further protect user privacy and prevent unauthorized location tracking. Starting with iOS 13, the CNCopyCurrentNetworkInfo API will no longer return valid Wi-Fi SSID and BSSID information. Instead, the information returned by default will be: SSID: “Wi-Fi” or “WLAN” (“WLAN" will be returned for the China SKU) BSSID: "00:00:00:00:00:00" If your app is using this API, we encourage you to adopt alternative approaches that don’t require Wi-Fi or network information. Valid SSID and BSSID information from CNCopyCurrentNetworkInfo will still be provided to VPN apps, apps that have used NEHotspotConfiguration to configure the current Wi-Fi network, and apps that have obtained permission to access user location through Location Services. Test your app on the latest iOS 13 beta to make sure it works properly. If your app requires valid Wi-Fi SSID and BSSID information to function, you can do the following: For accessory setup apps, use the NEHotSpotConfiguration API, which now has the option to pass a prefix of the SSID hotspot your app expects to connect to. For other types of apps, use the CoreLocation API to request the user’s consent to access location information. Learn more by reading the updated documentation or viewing the the Advances in Networking session video from WWDC19\. You can also submit a TSI for code-level support. Best regards, Apple Developer Relations
蘋果為了所謂隱私安全不讓直接獲取到wifiSSID了,然后還告知,如果是使用 NEHotspotConfiguration 的app可以獲取,另外其他類型app需要用CoreLocation請求位置權(quán)限,用戶同意后才可以獲取。
-
使用NEHotspotConfiguration的app
// 連接WiFi NEHotspotConfiguration *config = [[NEHotspotConfiguration alloc] initWithSSID:@"wifi名" passphrase:@"密碼" isWEP:NO]; NEHotspotConfigurationManager *manager = [NEHotspotConfigurationManager sharedManager]; [manager applyConfiguration: config completionHandler:^(NSError * _Nullable error) { NSLog(@"error :%@",error); }]; // 獲取wifiSSID - (NSString *)wifiSSID { NSString *wifiSSID = nil; CFArrayRef wifiInterfaces = CNCopySupportedInterfaces(); if (!wifiInterfaces) { return nil; } NSArray *interfaces = (__bridge NSArray *)wifiInterfaces; for (NSString *interfaceName in interfaces) { CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName)); if (dictRef) { NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef; wifiSSID = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID]; CFRelease(dictRef); } } CFRelease(wifiInterfaces); return wifiName; }
-
請求位置權(quán)限征求用戶同意后獲取wifiSSID
推薦使用封裝好的請求權(quán)限方式github.com/RainOpen/DD…
#import <SystemConfiguration/CaptiveNetwork.h> - (void)ddy_wifiSSID:(void (^)(NSString *wifiSSID))wifiSSID { void (^callBack)(NSString *) = ^(NSString *wifiName) { if (wifiSSID) { wifiSSID(nil); } }; void (^getWifiName)(void) = ^(){ CFArrayRef wifiInterfaces = CNCopySupportedInterfaces(); if (!wifiInterfaces) { callBack(nil); return; } NSString *wifiName = nil; for (NSString *interfaceName in (__bridge_transfer NSArray *)wifiInterfaces) { CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName)); if (dictRef) { NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef; wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID]; CFRelease(dictRef); } } CFRelease(wifiInterfaces); callBack(wifiName); }; if ([CLLocationManager locationServicesEnabled]) { [DDYAuthManager ddy_LocationAuthType:DDYCLLocationTypeAuthorized alertShow:YES success:^{ getWifiName(); } fail:^(CLAuthorizationStatus authStatus) { NSLog(@"定位服務(wù)被拒絕,彈窗告訴無法獲取wifiSSID,請到設(shè)置開啟定位權(quán)限"); callBack(nil); }]; } else { NSLog(@"定位服務(wù)不可用"); callBack(nil); } }
-
VPN舊版應(yīng)用
-
-
Xcode10往iOS13上編譯運行提示 Could not find Developer Disk Image
- 下載開發(fā)包
- 強制退出Xcode(必須退出干凈)
- 前往"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"粘貼解壓縮文件(以自己實際路徑實際名稱)
-
iOS 13 UITabBar上分割線呢操作
原來設(shè)置分割線的方式失效了
[[UITabBar appearance] setBackgroundImage:[UIImage new]]; [[UITabBar appearance] setShadowImage:[UIImage new]]; 復(fù)制代碼
最新更改TabBar上細(xì)線方式實例,利用蘋果提供的新API,為所欲為(改圖片,改顏色)
// OC if (@available(iOS 13, *)) { #ifdef __IPHONE_13_0 UITabBarAppearance *appearance = [self.tabBar.standardAppearance copy]; appearance.backgroundImage = [UIImage new]; appearance.shadowImage = [UIImage imageNamed:@"Dotted_Line"]; appearance.shadowColor = [UIColor clearColor]; self.tabBar.standardAppearance = appearance; #endif } else { self.tabBar.backgroundImage = [UIImage new]; self.tabBar.shadowImage = [UIImage imageNamed:@"Dotted_Line"]; } // Swift 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() }
-
暗黑模式
- 關(guān)于暗黑模式也是開發(fā)者可選擇性適配的內(nèi)容,這里不贅述了,提供個文章參考
- QiShare iOS13 DarkMode適配
- iOS13 暗黑模式(Dark Mode)適配之OC版
-
library not found for -l stdc++.6.0.9
Xcode10開始去除了C++6.0.9
如果非用不可,下載文件
// 文件夾 1、2、3、4 中的文件分別對應(yīng)復(fù)制到Xcode10中的以下4個目錄中即可(Xcode11目錄可能有變更) // 假設(shè)默認(rèn)安裝目錄且Xcode.app命名 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/ 復(fù)制代碼
更新Xcode11目錄變更
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/ ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 變更為 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/ 復(fù)制代碼
- 換個姿勢試試
1\. TARGETS–Build Phases–Link Binary With Libraries,刪除6.0.9依賴, 2\. 需要的話對應(yīng)添加libc++.tdb、libstdc++.tdb 3\. TARGETS–Build Settings–Other Linker Flags,刪除 -l”stdc++.6.0.9” 4\. 如果是第三庫引用了C++6.0.9庫,那就只能聯(lián)系服務(wù)方修改了 復(fù)制代碼
-
Multiple commands produce 'xxx/Info.plist'
- Xcode10開始變更編譯系統(tǒng),如果項目所在空間存在多個Info.plist則報錯
xcworkspace項目: Xcode左上角菜單欄 File –> Workspace Settings –> Build System – >Legacy Build System xcodeproj項目:Xcode左上角菜單欄 –> File –> Project Settings –> Build System –> Legacy Build System 復(fù)制代碼
-
升級Xcode后xib報錯 Failed to find or create execution context for description ...
- 可以萬能重啟,還可以。。。
sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService # 將你xcode中Developer文件夾位置放進(jìn)來 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer xcrun simctl erase all 復(fù)制代碼
-
友盟導(dǎo)致崩潰 +[_LSDefaults sharedInstance]
- 更新版本
- 如果暫時沒法更新(理由,我擦,屎山),臨時方案宇宙中轉(zhuǎn)站→Saylor_lone博客:iOS 13 適配 ING...
// 本工地大工沒實際驗證。。。 @implementation NSObject (DDYExtension) + (void)changeOriginalSEL:(SEL)orignalSEL swizzledSEL:(SEL)swizzledSEL { Method originalMethod = class_getInstanceMethod([self class], orignalSEL); Method swizzledMethod = class_getInstanceMethod([self class], swizzledSEL); if (class_addMethod([self class], orignalSEL, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) { class_replaceMethod([self class], swizzledSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL originalSEL = @selector(doesNotRecognizeSelector:); SEL swizzledSEL = @selector(ddy_doesNotRecognizeSelector:); [self changeOriginalSEL:originalSEL swizzledSEL:swizzledSEL]; }); } + (void)ddy_doesNotRecognizeSelector:(SEL)aSelector{ // 處理 _LSDefaults 崩潰問題 if([[self description] isEqualToString:@"_LSDefaults"] && (aSelector == @selector(sharedInstance))){ //冷處理... return; } [self ddy_doesNotRecognizeSelector:aSelector]; } 復(fù)制代碼
-
UITextField的leftView和rightView設(shè)置UIImageView或UIButton等被系統(tǒng)強(變)奸(窄)了。。。
- 臨時解決方案,交換leftView/rightView的getter、setter,
- 然后包裝一個containerView父視圖,并將containerView給了相應(yīng)左右視圖
- 取視圖則先取出containerView,從containerView中取出真正想要的視圖
- 注意處理在containerView上的位置。。。
-
UIScrollView滾動條指示器偏移
// 屏幕旋轉(zhuǎn)可能會觸發(fā)系統(tǒng)對滾動條的自動修正,如果沒有修改需求,關(guān)閉該特性即可 #ifdef __IPHONE_13_0 if (@available(iOS 13.0, *)) { self.automaticallyAdjustsScrollIndicatorInsets = NO; } #endif 復(fù)制代碼
-
WKWebView 中測量頁面內(nèi)容高度的方式變更
iOS 13以前 document.body.scrollHeight iOS 13開始 document.documentElement.scrollHeight
-
CBCenterManager 藍(lán)牙使用權(quán)限
iOS13之前不用申請權(quán)限,iOS13開始需要申請權(quán)限
<key>NSBluetoothAlwaysUsageDescription</key> <string>App想使用藍(lán)牙,是否同意</string>` 復(fù)制代碼
-
Xcode10的過時問題,蟲洞傳送門
-
Xcode11 創(chuàng)建新工程在AppDelegate.m中設(shè)置window不生效
- Xcode11把入口交給了scene(為SwiftUI多場景打基礎(chǔ)的)
- 想要換回AppDelegate入口
- 打開Info.plist點擊減號刪除Application Scene Mainfest
- 再刪除SceneDelegate兩個文件
- 刪除AppDelegate.m中UISceneSession lifecycle兩個方法
- 最后AppDelegate.h中添加 @property (strong, nonatomic) UIWindow *window;