iOS

Tips

在不断填坑中前进。。

Posted by 三十一 on June 28, 2052

Tips

1. TARGET_IPHONE_SIMULATOR

经常集成的第三方库,有些不支持模拟器,每一次调试代码要么就是一直连接真机,要么就是,把这个库相关的代码屏蔽,其实有更优雅的解决办法:TARGET_IPHONE_SIMULATOR

使用示例如下:

#if TARGET_IPHONE_SIMULATOR
#else
//只支持真机的第三方库
#import <LopeKit/LopeKit.h>
#endif



#if TARGET_IPHONE_SIMULATOR
//模拟器编译代码
@interface BluetoothManager()
<
CBCentralManagerDelegate
>
#else
//非模拟器编译代码
@interface BluetoothManager()
<
NPDSophKeyDelegate,//只支持真机的第三方库代理
CBCentralManagerDelegate
>
#endif

2. 构建版本一直不显示

从iOS10开始,苹果更加注重对用于隐私的保护,app里边如果需要访问用户隐私,必须要做相应隐私权限的描述,所以要在plist文件中添加描述。 下面是几种权限的描述,按需添加:

  • 麦克风权限:Privacy - Microphone Usage Description 是否允许此App使用你的麦克风?
  • 相机权限: Privacy - Camera Usage Description 是否允许此App使用你的相机?
  • 相册权限: Privacy - Photo Library Usage Description 是否允许此App访问你的媒体资料库?
  • 通讯录权限: Privacy - Contacts Usage Description 是否允许此App访问你的通讯录?
  • 蓝牙权限:Privacy - Bluetooth Peripheral Usage Description 是否允许此App使用蓝牙?
  • 语音转文字权限:Privacy - Speech Recognition Usage Description 是否允许此App使用语音识别?
  • 日历权限:Privacy - Calendars Usage Description 是否允许此App使用日历?
  • 定位权限:Privacy - Location When In Use Usage Description 我们需要通过您的地理位置信息获取您周边的相关数据

3. IDFA

友盟的提供的无 IDFA 版本的 SDK。 使用命令行检查含有 IDFA,但是审核的时间选择 ,又不影响审核。🤣🤣

➜  copm_staff grep -r advertisingIdentifier .
Binary file ./copm_staff/Business/lib/Share/UMSocial/UMSocialSDK/UMSocialCore.framework/UMSocialCore matches
Binary file ./copm_staff/Expand/UMessage/UMessage_Sdk_1.4.0/libUMessage_Sdk_1.4.0.a matches
Binary file ./copm_staff/Expand/UMessage/UMessage_Sdk_1.5.0a/libUMessage_Sdk_1.5.0a.a matches
Binary file ./UMMobClick.framework/UMMobClick matches
Binary file ./UMMobClick.framework/Versions/A/UMMobClick matches
Binary file ./UMMobClick.framework/Versions/Current/UMMobClick matches

4. 生成好看的代码图片

这个网站:https://carbon.now.sh/ 可以把代码生成一种很好看的格式。

test

5. 安装破解的文件

“通用”里有时没有“任何来源”这个选项:

显示”任何来源”选项在控制台中执行: sudo spctl --master-disable

不显示”任何来源”选项(macOS 10.12默认为不显示)在控制台中执行: sudo spctl --master-enable

6. 近距离感应器

// 打开距离传感器
[UIDevice currentDevice].proximityMonitoringEnabled = YES;
// 通过通知监听有物品靠近还是离开
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateDidChange:) name:UIDeviceProximityStateDidChangeNotification object:nil];

- (void)proximityStateDidChange:(NSNotification *)note
{
    if ([UIDevice currentDevice].proximityState) {
        NSLog(@"有物品靠近");
    } else {
        NSLog(@"有物品离开");
    }
} 

7. 使用第三方库上层再来一次封装

使用第三方库上层再来一次封装,不要直接使用第三方库,这样的好处是替换第三方库的成本会低很多。譬如使用 AFNetworking,MJRefresh等。如果做了一层封装,以后替换就只需要修改封装的上层代码。

8. source 命令

在编译核心时,常常要反复输入一长串命令,如

make mrproper
make menuconfig
make dep
make clean
make bzImage
.......

这些命令既长,又繁琐。而且有时候容易输错,浪费你的时间和精力。如果把这些命令做成一个文件,让它自动按顺序执行,对于需要多次反复编译核心的用户来说,会很方便。 用source命令可以办到这一点。它的作用就是把一个文件的内容当成是shell来执行。

shell 编程中的命令有时和C语言是一样的。&& 表示与,|| 表示或。把两个命令用 && 联接起来,如 make mrproper && make menuconfig,表示要第一个命令执行成功才能执行第二个命令。对执行顺序有要求的命令能保证一旦有错误发生,下面的命令不会盲目地继续执行。

9. AF 报错 Code=-1016

这个是因为通讯通里面包含的 content-type 包含的不是你支持的几种格式,一般我们用 json 指定响应格式,但是服务端经常返回的通讯头 content-type 值是:text/plain,AF默认的响应是不支持这种响应的,要么是服务端改,要么是客户端改。。。但一般都客户端改。。。

一般会把 AFJSONResponseSerializer 里面改为

    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"text/plain", nil];

改了以后,有时候会有 Code=3840 的错误,这个一般是因为服务端返回的字符串带有转义字符导致的,没法解析出json

10. Mac OS 如何更改文件的默认打开方式

选中文件 邮件显示编辑菜单,点击显示简介(快捷键: ⌘ + i)。 然后会有一个打开方式的选项,更改后,点击下面的全部更改,以后打开这种文件,就默认使用你设定的App了。

11. 蒲公英、Fir 分发平台 无法直接替换 App Store 版本

今天测试告诉我,扫码安装测试包,不能像之前一样替换了,就搜索了一下,有两种场景,分别是蒲公英平台

为什么在 iOS 9 中,点击“安装”按钮后,没有任何反应,桌面也没有出现应用图标,但是状态栏上的网络图标在转?

这是由于 iOS 9 中的一个 Bug 造成的。虽然看上去没有反应,其实应用已经在后台开始下载并安装了,状态栏上的网络图标在转就是一个证明。这个时候,只要多等待一会儿就好了,应用安装完成之后会在桌面上显示出来的。

fir平台

第五种:设备上已安装了该应用,并且与当前要安装的应用使用的证书不同

解决方法:删除旧的应用,重新安装

无法替换 App Store 版本的这种情景就属于第二种,App Store版本的证书与测试版本的证书不一致导致的。

12. 计算滑动距离

- (void)panGesture:(UIPanGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateBegan) {
        startLocation = [sender locationInView:self.view];
    }
    else if (sender.state == UIGestureRecognizerStateEnded) {
        CGPoint stopLocation = [sender locationInView:self.view];
        CGFloat dx = stopLocation.x - startLocation.x;
        CGFloat dy = stopLocation.y - startLocation.y;
        CGFloat distance = sqrt(dx*dx + dy*dy );
        NSLog(@"Distance: %f", distance);
    }
}

13. show invisibles 显示对齐符

14. 一次删除多个 python 第三方库

之前下了一个 pyobjc的第三方库,但是又没用,就想把它删除,使用 pip list 看了一下,发现它安装了好几十个依赖库,如果一个个删除 pip uninstall 当然也可以,但是有一种更简单的方法。

就是第八条提到的 source 命令。

1.首先找出要卸载的第三方库

因为要卸载的库都是以 pyobjc- 开通的,使用 这个 命令看下要卸载的库有哪些,pip list | grep "pyobjc-*"

pyobjc-core 
pyobjc-framework-Accounts 
pyobjc-framework-SyncServices 
...
...
...
pyobjc-framework-SystemConfiguration 
pyobjc-framework-WebKit 

2. 把要卸载的库名称保存到一个本地文件中

找到了要卸载的库名称,把它们保存到一个本地文件中,然后在开头统一添加前缀 pip uninstall,sublime 可以这样操作,先选中全部文本,然后 command + Shift + L 进入多行编辑模式,然后 按 方向键 ,就可以多行添加了。

把要执行的命令保存到一个本地文本假设叫做 a.txt。

pip uninstall pyobjc-core 
pip uninstall pyobjc-framework-Accounts 
pip uninstall pyobjc-framework-AddressBook 
pip uninstall pyobjc-framework-AppleScriptKit 
pip uninstall pyobjc-framework-AppleScriptObjC 
...
...
...
pip uninstall pyobjc-framework-StoreKit 
pip uninstall pyobjc-framework-SyncServices 
pip uninstall pyobjc-framework-SystemConfiguration 
pip uninstall pyobjc-framework-WebKit 

3. 使用 source 命令执行

进入 a.txt 所在的目录,执行命令 source a.txt

15. 文字绘制到屏幕上

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(ctx, kCGBlendModeCopy);
    CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 0.0);
    CGContextSetLineWidth(ctx, 10.f);
    CGContextSetCharacterSpacing(ctx, 10.f);
    CGContextSetTextDrawingMode(ctx, kCGTextFill);
    NSString *s = @"哈哈🐲";
    [s drawAtPoint:CGPointMake(0, 50) withAttributes:@{NSFontAttributeName :[UIFont systemFontOfSize:40],NSForegroundColorAttributeName:[UIColor clearColor]}];

}

16. epub 转换为 mobi 格式

16.1 下载 KindleGen

去这个地址现在 KindleGen

16.2 设置 shell 别名

可以不设置,但是每次使用这个软件,都有进入 KindleGen 所在的文件夹再操作。

.zshrc 文件中输入(我使用的是 iterm 2 ,其他终端设置别名可能不一样)

alias kindle='cd /Applications/KindleGen_v2_9;./kindlegen '
alias Kindle='cd /Applications/KindleGen_v2_9;./kindlegen '

/Applications/KindleGen_v2_9KindleGen 所在的路径

使用:

 kindle your_file_path.epub

会生成 mobi 文件到 epub 文件所在的目录

17. 实用快捷键

系统

  • Command+Ctrl+d 启用字典翻译,触摸板三指轻点。
  • Option + Esc 朗读选中文本(需自己去开启,路径:系统偏好设置-辅助功能-语音,就能看到一个 按下按键时朗读所选文本 的选项,勾选上就好了,我自己设置成了Option + `)。

Xcode

  • Command+L 跳转到指定代码行
  • Command + Shift + o 打开指定文件
  • Command + Shift + j 左侧文件目录栏 显示当前编辑的文件。
  • Ctrl + Command + ↑/↓ 切换 .h 和 .m 文件
  • Ctrl + a/e/n/b/f/p 移动光标 (MAC 系统通用)
  • Ctrl + Command + e 批量修改变量名

test

  • 在上图中提示代码选项之间的切换 Ctrl + n/p,在mac 系统中通用,切换菜单都可以使用这个快捷键

18. Mac 预览/Preview 启动后就卡死的问题

打开 ~/Library/Containers 目录。

找到 com.apple.preview 相关目录,删掉后,重新打开 预览/Preview 就可以正常使用了。

19. application loader 无法连接到服务器

如果近期出现 “application loader 无法连接到服务器” 的问题,同时你又设置了 chrome 为默认浏览器的,可以把默认浏览器修改为 Safari 试试看能不能解决这个问题。 同时如果你出现 chrome 无法打开 itc 或者任何苹果开发者的网站,请使用 Safari 打开,应该会解决问题。 🍎💊 🍎💊 🍎💊 🍎💊

20. Guideline 2.1 - Information Needed

当审核被拒时,原因是 Information Needed,这种情况是苹果需要你确认一些信息,如果你确认你的App没有违反邮件里面列出的条款,回复邮件向苹果承诺他们要求的信息就可以了。不用撤下来发新包。血的教训,因为英文比较差,没领会到苹果爸爸的意思,直接耽误了一个星期发版本的时间。

21. mac 修改本地DNS 地址解析

$ cd /private/etc
$ open .

然后找到 hosts 文件,copy出来一份编辑。 譬如添加一行 45.63.1.188 ssy.com

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost 
127.0.0.1 windows10.microdone.cn
127.0.0.1 domain_used

45.63.1.188 ssy.com

以后再访问45.63.1.188 这个 ip 地址,只需要输入 ssy.com 就行了。只在本机生效

22. 获取 启动图 launchImage 图片

+ (UIImage *)getTheLaunchImage
{
    CGSize viewSize = [UIScreen mainScreen].bounds.size;
    
    NSString *viewOrientation = nil;
    if (([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown) || ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait)) {
        viewOrientation = @"Portrait";
    } else {
        viewOrientation = @"Landscape";
    }
    
    
    NSString *launchImage = nil;
    
    NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
    for (NSDictionary* dict in imagesDict)
    {
        CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
        
        if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
        {
            launchImage = dict[@"UILaunchImageName"];
        }
    }
    
    return [UIImage imageNamed:launchImage];
    
}

23. Register Multiple Devices

选中 Register Multiple Devices test 按照下面的文本格式写就行了,文件名保存为 multiple-device-upload-ios.txt,其他名称没有试。

Device ID	Device Name
XXXX	unknown_05
XXXX	unknown_06
XXXX	unknown_07
XXXX	unknown_08

24. 莫名其妙的错误信息

如果出现了下面的错误信息,有茫然没有头绪,除了网上说的哪几种解决方法外,还有一种可能。 就是观察一个对象后,观察者销毁后没有移除观察者模式。这就是我今天查了半天才查到的一个崩溃问题。

invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.
libc++abi.dylib: terminate_handler unexpectedly threw an exception

25 查找特定名称的文件夹,并拷贝文件夹下的文件到指定目录

查找目录下特定名称的文件夹,并将文件夹下的文件移动到响应名称的文件夹下。

#! /bin/zsh
# 在当前目录下查找名称为 Images 的文件夹
# 读作变量 f
find . -type d  -name Images | while read f; do
    # 取出上一个文件夹的名称
    resFile=`echo $f | awk -F "/" '{print $4}'`
    echo $resFile
    # 创建相应的文件夹
    mkdir -p ./123/$resFile
    #拷贝 f 文件夹下的所有文件 到新创建的文件夹
    cp  $f/* ./123/$resFile/
done
#! /bin/zsh
mypath=`pwd`
echo $mypath
for dic in `ls $mypath/test/`
do
   # 读取文件夹名称的前缀 文件夹名称为 9.1 读取 9
   resFile=`echo $dic | awk -F "." '{print $1}'`
   # 拼接目标路径
   p=${mypath}/Projet/Chapter${resFile}/
   echo $p
   #拷贝文件到目标路径
   cp -r $mypath/test/$dic $p
done

26 建立ssh信任关系

本地机器 A(Mac) 远程登录机器 B(Linux),每次都要输入密码,很烦躁,搜索后发现可以建立两个机器之间的信任关系,具体步骤如下:

  1. A 中 进入 ~/.ssh/
  2. 运行 ssh-keygen,所有的提示都默认回车即可
  3. 完成后会多出来一个 id_rsa.pub 的文件。
  4. scp -r id_rsa.pub root@1.2.3.4:/root/.ssh/authorized_keys root 是 B 机器的账户
  5. 输入 B 机器 root 账户密码。
  6. 搞定

如果提示 scp: /root/.ssh/authorized_keys: No such file or directory 需要先在远程登录机器 B(Linux)root 目录(root 账户登录后的默认目录)建立一个 .ssh 的文件夹,mkdir .ssh

再次远程登录 B 后,再也不需要输入密码了。

27 Xcode sort by name

不知道从哪个版本,当你重新组织下代码的目录结构,然后删除添加到原工程中时,源文件的排列结构就像下面一样混乱。 通常我们可以通用右键 sort by name 来重新排列 也可以设置快捷键来快速完成这个常用的操作

网上找到脚本自动设置的方案没有成功。地址如下:How to automatically sort by name in Xcode project

28 UITableViewCell 自适应高度 在 iOS 10.3.x 出现页面错乱

检查如下几项:

  1. 是否包含多行 UILabel,如果包含,设置 preferredMaxLayoutWidth 属性。
  2. UITableView 设置 estimatedRowHeightrowHeight = UITableViewAutomaticDimension;

29 获取 statusBar 高度的宏定义

#define SY_StatusHight \
({\
CGRect rectStatus = [[UIApplication sharedApplication] statusBarFrame];\
rectStatus.size.height;\
})\

30 UIButton remove all target-actions

UIButton remove all target-actions

[someControl removeTarget:nil 
                   action:NULL 
         forControlEvents:UIControlEventAllEvents];

31 convert NSDictionary to NSString

这个写法太简洁了。

[NSString stringWithFormat:@"my dictionary is %@", aDictionary];

convert NSDictionary to NSString

32 利用正则搜索项目中的中文字符

commend + shift + f 进入搜索页面,选择 Find -> Regular Expression ,输入 \p{Han}+ 可以搜索项目中的中文字符。

33 UITableViewCell 内容改变时,动态修改高度

设置好约束条件后,如果 cell 的内容发生改变了,这个时候需要实时的更新页面高度,做法是:

    [weakSelf.parentTableView beginUpdates];
    [weakSelf.parentTableView endUpdates];

34 UIViewController 不准确的 self.view.frame

UIViewController 的生命周期中,viewDidLoadviewWillAppearviewDidAppear中,获取 self.view.frame 的值是不同的。

调用方法 viewDidLoad 中,所有的控件的 frame 都是在当前 storyboard 中状态,所以获取到是数值是不对的。 setFrame 的方法一定要放在 viewDidAppear 里,在 viewDidLoad 里是没有效果的。因为 viewDidLoad 的时候, view 还没有加载完成。

记一次坑。由于我的手机是 X,然后 storyboard 里面我选择的也是 X ,开发中就没发现这个问题。

35 找到页面特定的标签,然后出发相应的事件

  var allclasss=document.getElementsByTagName('button');
  var allcspanlasss=document.getElementsByClassName('span')
  var allcspanlasss=document.getElementById("sp").click();

  for(var count = 0; count < allclasss.length; count++){
	var button = links[count];
   	button.click()
  }

36 优雅的根据字符串判断代码执行

一般来说 OC 对字符串没有很好的办法,大多数会这样写。

NSString *str;
	if ([str isEqualToString:@""]) {
		code
	}else if (str isEqualToString:@"")
	{
		code
	}
	......

由于现在做的一个项目有大量的这种判断,就搜了一下有没有更优雅的写法,找到了下面的这种写法,感觉好了很多。

原文地址

	NSDictionary *d = @{
		@"Diamonds":
		^{
			YX_after_run(3, {
				HUDError(@"测试测试测试", nil);
			})
		},
		@"Hearts":
		^{
			
		},
		@"Clubs":
		^{
			
		},
		@"Spades":
		^{
			
		}
	};
	typedef void (^CaseBlock)(void);
	//匹配 执行
	((CaseBlock)d[@"Diamonds"])();
	
	// default
	CaseBlock c = d[lookup];
 if (c) c(); else { NSLog(@"Joker"); }

37 加载 HTML 文件时,<img> 标签不显示的问题

有时候我们加载 HTMl 的时候,并不是加载链接地址,而是服务端直接返回了 HTML 字符串给你[黑人问号.jpg],咱也搞不懂为什么不直接返回一个链接地址,脱裤子放屁多次一举?不管为什么这样,反正已经是这个情况了,然后就老老实实的加载字符串:

		[self.webView loadHTMLString:htmlString baseURL:nil];

然后有一个问题,<img> 标签的网络图片不显示。找了半天解决方案,绕了一些弯路,最终解决方法如下,在 info.plist 文件加入这个选项就行了。

	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoadsInWebContent</key>
		<true/>
	</dict>

ps 原来只是因为这个链接是 HTTP 链接!!!!!

tableHeaderView 为 xib 时,如何设置 frame

如果 xib 生成的 view 不在外面包裹一层的话,加载出来的头视图会莫名其妙被拉大!!

	let headView = UITableViewHeaderFooterView.init(frame: CGRect.init(x: 0, y: 0, width: SCREEN_WIDTH, height: 180))
	if let xibHeadView = xibHeadView {
		headView.addSubview(xibHeadView)
		xibHeadView.snp.makeConstraints({$0.edges.equalToSuperview()})
	}
	tableView.tableHeaderView = headView