本文对应github地址iOS 13 问题解决以及苹果登录,如果由于github调整导致资源找不到或细节更改,请访问github
本文掘金地址
本文直接搬砖,随便看看就行
iOS 13 (Xcode11编译时)问题解决以及苹果登录
如 [textField setValue:color forKeyPath:@"_placeholderLabel.textColor"];
会crash。
那么该怎么办呢?下面提供几种姿势
_textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : color}];
// 必须new创建,如果alloc-init创建还是crash(两种方式差别自行google,不是BD)
UILabel * placeholderLabel = [UILabel new];
placeholderLabel.text = @"666";
placeholderLabel.textColor = [UIColor blueColor];
[_textField setValue: placeholderLabel forKey:@"_placeholderLabel"];//new创建这里并没有crash
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(_textField, ivar);
placeholderLabel.textColor = color;
- (UITextField *)ddy_SearchField {
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
return self.searchTextField;
}
#endif
return [self valueForKey:@"_searchField"];
}
所以修改UISearchBar占位字符可以把上面的结合使用
该分类所在github工程
UIViewController+DDYPresent.h下载该文件
/// 一个一个改浪费时间,适合版本迭代中逐步替换;
/// 直接重写-modalPresentationStyle 侵入性太大,造成系统弹出也被重置,或者某个控制器想改变样式都不能,不太友好
/// 所以用一个类方法控制全局,一个实例方法控制具体某个控制器实例样式。
#import
NS_ASSUME_NONNULL_BEGIN
@interface UIViewController (DDYPresent)
/// 自动调整模态弹出样式时要排除的控制器(如果未设置则使用内置)
/// @param controllerNameArray 模态弹出的控制器名称数组
+ (void)ddy_ExcludeControllerNameArray:(NSArray *)controllerNameArray;
/// 是否自动调整模态弹出全屏样式
/// NO:表示不自动调整,保持默认,可能全屏样式也可能其他样式
/// YES:表示调整为全屏样式
/// 如果是排除的控制器数组包含的控制器则默认NO
/// 如果不在排除的控制器数组内包含则默认YES
@property (nonatomic, assign) BOOL ddy_AutoSetModalPresentationStyleFullScreen;
@end
NS_ASSUME_NONNULL_END
UIViewController+DDYPresent.m下载该文件
#import "UIViewController+DDYPresent.h"
#import
@implementation UIViewController (DDYPresent)
static NSArray *excludeControllerNameArray;
+ (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_AutoSetModalPresentationStyleFullScreen) {
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
}
[self ddy_PresentViewController:viewControllerToPresent animated:flag completion:completion];
}
- (void)setDdy_AutoSetModalPresentationStyleFullScreen:(BOOL)ddy_AutoSetModalPresentationStyleFullScreen {
objc_setAssociatedObject(self, @selector(ddy_AutoSetModalPresentationStyleFullScreen), @(ddy_AutoSetModalPresentationStyleFullScreen), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)ddy_AutoSetModalPresentationStyleFullScreen {
NSNumber *obj = objc_getAssociatedObject(self, @selector(ddy_AutoSetModalPresentationStyleFullScreen));
return obj ? [obj boolValue] : ![UIViewController ddy_IsExcludeSetModalPresentationStyleFullScreen:NSStringFromClass(self.class)];
}
// MARK: - 类方法
// MARK: 全局设置排除的控制器
+ (void)ddy_ExcludeControllerNameArray:(NSArray *)controllerNameArray {
excludeControllerNameArray = controllerNameArray;
}
// MARK: 如果没有外部设置则使用内置的排除数组
+ (NSArray *)ddy_InnerExcludeControllerNameArray {
NSMutableArray *nameArray = [NSMutableArray array];
[nameArray addObject:@"UIImagePickerController"];
[nameArray addObject:@"UIAlertController"];
[nameArray addObject:@"UIActivityViewController"];
[nameArray addObject:@"UIDocumentInteractionController"];
[nameArray addObject:@"SLComposeViewController"]; // #import
[nameArray addObject:@"SLComposeServiceViewController"]; // #import
[nameArray addObject:@"UIMenuController"];
[nameArray addObject:@"SFSafariViewController"]; // API_AVAILABLE(ios(9.0)) #import
[nameArray addObject:@"SKStoreReviewController"]; // API_AVAILABLE(ios(10.3), macos(10.14)) #import
[nameArray addObject:@"SKStoreProductViewController"]; // API_AVAILABLE(ios(6.0)) #import
return nameArray;
}
// MARK: 是否是要排除自动设置的控制器
+ (BOOL)ddy_IsExcludeSetModalPresentationStyleFullScreen:(NSString *)className {
NSArray *nameArray = excludeControllerNameArray ?: [UIViewController ddy_InnerExcludeControllerNameArray];
return [nameArray containsObject:className];
}
@end
NSString *myToken = [deviceToken description];
myToken = [myToken stringByReplacingOccurrencesOfString: @"<" withString: @""];
myToken = [myToken stringByReplacingOccurrencesOfString: @">" withString: @""];
myToken = [myToken stringByReplacingOccurrencesOfString: @" " withString: @""];
为什么不建议这样写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);
}
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的点击加入群聊iOS交流群:789143298 ,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术,
大家一起交流学习成长!
##### 推荐的写法
```
- (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);
}
```
去开发者网站 在 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); // 宽度过小就没有文字了,只剩图标
[self.view addSubview:button];
ASAuthorizationControllerPresentationContextProviding
#pragma mark - ASAuthorizationControllerPresentationContextProviding
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0))
{
return self.view.window;
}
Authorization 发起授权登录请求
- (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];
}
授权回调处理
#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 = @"用户取消了授权请求";
break;
case ASAuthorizationErrorFailed:
errorMsg = @"授权请求失败";
break;
case ASAuthorizationErrorInvalidResponse:
errorMsg = @"授权请求响应无效";
break;
case ASAuthorizationErrorNotHandled:
errorMsg = @"未能处理授权请求";
break;
case ASAuthorizationErrorUnknown:
errorMsg = @"授权请求失败未知原因";
break;
}
NSLog(@"%@", errorMsg);
}
User ID: Unique, stable, team-scoped user ID,苹果用户唯一标识符,该值在同一个开发者账号下的所有 App 下是一样的,开发者可以用该唯一标识符与自己后台系统的账号体系绑定起来。
Verification data: Identity token, code,验证数据,用于传给开发者后台服务器,然后开发者服务器再向苹果的身份验证服务端验证本次授权登录请求数据的有效性和真实性,详见 Sign In with Apple REST API。如果验证成功,可以根据 userIdentifier 判断账号是否已存在,若存在,则返回自己账号系统的登录态,若不存在,则创建一个新的账号,并返回对应的登录态给 App。
Account information: Name, verified email,苹果用户信息,包括全名、邮箱等。
Real user indicator: High confidence indicator that likely real user,用于判断当前登录的苹果账号是否是一个真实用户,取值有:unsupported、unknown、likelyReal。
失败情况会走 authorizationController:didCompleteWithError: 这个方法,具体看代码吧
其他情况的处理
这些情况下,App 需要获取到这些状态,然后做退出登录操作,或者重新登录。
我们需要在 App 启动的时候,通过 getCredentialState:completion: 来获取当前用户的授权状态
- (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 解析如下:
还可以通过通知方法来监听 revoked 状态,可以添加 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 里,我们在使用的时候,只需要在创建 request 的时候,多创建一个 ASAuthorizationPasswordRequest ,这样如果 KeyChain 里面也有登录信息的话,可以直接使用里面保存的用户名和密码进行登录。代码如下
- (void)perfomExistingAccountSetupFlows API_AVAILABLE(ios(13.0))
{
ASAuthorizationAppleIDProvider *appleIDProvider = [ASAuthorizationAppleIDProvider new];
ASAuthorizationAppleIDRequest *authAppleIDRequest = [appleIDProvider createRequest];
ASAuthorizationPasswordRequest *passwordRequest = [[ASAuthorizationPasswordProvider new] createRequest];
NSMutableArray * array = [NSMutableArray arrayWithCapacity:2];
if (authAppleIDRequest) {
[array addObject:authAppleIDRequest];
}
if (passwordRequest) {
[array addObject:passwordRequest];
}
NSArray * 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);
}
}
官方Demo
错误信息 Device doesn’t support wireless sync. AMDeviceStartService(device, CFSTR(“com.apple.debugserver”), &gdbfd, NULL)
解决方案
更新Flutter
flutter upgrade
暂时切换到dev或master
flutter channel dev
// 或下面
// flutter channel master
// 然后执行
flutter doctor
// dart2.6 flutter1.10
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请求位置权限,用户同意后才可以获取。
// 连接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;
}
推荐使用封装好的请求权限方式github.com/RainOpen/DD…
#import
- (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(@"定位服务被拒绝,弹窗告诉无法获取wifiSSID,请到设置开启定位权限");
callBack(nil);
}];
} else {
NSLog(@"定位服务不可用");
callBack(nil);
}
}
原来设置分割线的方式失效了
[[UITabBar appearance] setBackgroundImage:[UIImage new]];
[[UITabBar appearance] setShadowImage:[UIImage new]];
最新更改TabBar上细线方式实例,利用苹果提供的新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()
}
self.tabBar.tinColor = color;
if (@available(iOS 10.0, *)) {
self.tabBar.unselectedItemTintColor = color;
}
Xcode10开始去除了C++6.0.9
如果非用不可,下载文件
// 文件夹 1、2、3、4 中的文件分别对应复制到Xcode10中的以下4个目录中即可(Xcode11目录可能有变更)
// 假设默认安装目录且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/
更新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/
1\. TARGETS–Build Phases–Link Binary With Libraries,删除6.0.9依赖,
2\. 需要的话对应添加libc++.tdb、libstdc++.tdb
3\. TARGETS–Build Settings–Other Linker Flags,删除 -l”stdc++.6.0.9”
4\. 如果是第三库引用了C++6.0.9库,那就只能联系服务方修改了
xcworkspace项目: Xcode左上角菜单栏 File –> Workspace Settings –> Build System – >Legacy Build System
xcodeproj项目:Xcode左上角菜单栏 –> File –> Project Settings –> Build System –> Legacy Build System
sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService
# 将你xcode中Developer文件夹位置放进来
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcrun simctl erase all
// 本工地大工没实际验证。。。
@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];
}
- (CGRect)leftViewRectForBounds:(CGRect)bounds {
CGRect iconRect = [super leftViewRectForBounds:bounds];
iconRect.origin.x = 3; // 可用属性控制
iconRect.size.width = 6; // 可用属性控制
return iconRect;
}
UIScrollView滚动条指示器偏移
// 屏幕旋转可能会触发系统对滚动条的自动修正,如果没有修改需求,关闭该特性即可
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
self.automaticallyAdjustsScrollIndicatorInsets = NO;
}
#endif
WKWebView 中测量页面内容高度的方式变更
iOS 13以前 document.body.scrollHeight iOS 13开始 document.documentElement.scrollHeight
CBCenterManager 蓝牙使用权限
iOS13之前不用申请权限,iOS13开始需要申请权限
NSBluetoothAlwaysUsageDescription
App想使用蓝牙,是否同意 `
参考
- iOS 13 适配 ING…
- iOS13 [UIViewController presentViewController]样式适配
020 持续更新,精品小圈子每日都有新内容,干货浓度极高。
结实人脉、讨论技术 你想要的这里都有!
抢先入群,跑赢同龄人!(入群无需任何费用)
BAT大厂面试题、独家面试工具包,
资料免费领取,包括 数据结构、底层进阶、图形视觉、音视频、架构设计、逆向安防、RxSwift、flutter,