
前言 哎,每次过完节都要有一个坑给自己跳。逃不过这个魔爪。这不,一过完春节,回来就发现公司证书出现"此证书的签发者无效"。 问题原因 经过一番查找,苹果官方给出了回答。 Thanks for bringing this to the attention of the community and apologies for the issues you’ve been having. This issue stems from having a copy of the expired WWDR Intermediate certificate in both your System and Login keychains. To resolve the issue, you should first download and install the new WWDR intermediate certificate (by double-clicking on the file). Next, in the Keychain Access application, select the System keychain. Make sure to select “Show Expired Certificates” in the View menu and then delete the expired version of the Apple Worldwide Developer Relations Certificate Authority Intermediate certificate (expired on February 14, 2016). Your certificates should now appear as valid in Keychain Access and be available to Xcode for submissions to the App Store. 看来是由于苹果系统的安全证书过期问题导致。 苹果早在一个多月前就提醒开发者使用新证书。开发者如果不及时更新,不仅会影响到自己的开发工作,还有可能对用户造成困扰。这一份通知面向的是那些开发应用与 Apple Wallet、Safari 推送中心又或者是 Safari 插件相关的开发者。苹果还在通知中表示,开发者需要在 2016 年 2 月 14 日结束之前更新他们的安全证书。更新之后,安全证书的下一个失效期为 2023 年 2 月。 解决办法 既然知道了原因,那么就可以对症下药了。我们通过下载苹果提供的最新的安全证书并双击安装。 然后打开钥匙串,在导航栏上,依次选择显示-显示已过期的证书  选择登录-所有项目 在搜索栏下输入 apple w 删除对应过期的安全证书 再一次感谢您花费时间阅读这篇文章! 微博: @Danny_吕昌辉 博客: SuperDanny
自身遇到问题 拓展-进行增删数据时的注意点 beginUpdates方法和endUpdates方法是什么呢? 一般什么时候使用这么一个动画块呢? 插入指定的行 插入分组到制定位置 删除制定位置的分组 移动分组 问答区 自身遇到问题 今天在设计自定义UITableViewCell的时候遇到一个愚蠢的问题,就是当用户在Cell上面使用自定义按钮触发删除时,为了保证删除时有动画效果,所以不能在执行deleteRowsAtIndexPaths: withRowAnimation:之后手动执行reloadData。这样就有一个问题,由于不执行reloadData,我们需要获取到Cell最新的indexPath就遇到问题 解决方案:通过获取按钮所在的Cell的indexPath解决 方式一: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AMDeleteCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath]; [cell setDeleteBlock:^(NSDictionary *dic, AMDeleteCell *cell) { //UITableViewCellContentView UIView *v = [sender superview]; //UITableViewCell UITableViewCell *cell = (UITableViewCell*)[v superview]; NSIndexPath *indexPath = [tableView indexPathForCell:cell]; self.deleteIndexPath = indexPath; }]; return cell; } 方式二(个人认为比较好的一种方式): - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AMDeleteCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath]; [cell setDeleteBlock:^(NSDictionary *dic, AMDeleteCell *cell) { NSIndexPath *indexPath = [tableView indexPathForCell:cell]; self.deleteIndexPath = indexPath; }]; return cell; } 自定义Cell内部部分代码: #import "AMDeleteCell.h" typedef void(^DeleteCellBlock)(NSDictionary *dic, AMDeleteCell *cell); @interface AMDeleteCell () @property (strong, nonatomic) UIButton *deleteBtn; @property (strong, nonatomic) NSDictionary *dataDic; @property (copy, nonatomic ) DeleteCellBlock block; @end @implementation AMDeleteCell //…省略部分代码... - (void)setDeleteBlock:(void (^)(NSDictionary *, AMDeleteCell *))block { self.block = block; } #pragma mark - Cell上面按钮触发的方法 - (void)deleteAction:(UIButton *)sender { if (_block) { _block(_dataDic, self); } } //...省略部分代码... @end 拓展-进行增删数据时的注意点 1、普通形式(UITableViewStylePlain)数据增删 将tableView进行增删时需要先更新数据源,然后再执行delete/insert操作 2、分组形式(UITableViewStyleGrouped)数据增删 如果我们的UITableView是UITableViewStyleGrouped的时候,我们如果删除某个Group的最后一条记录时,相应的分组也将被删除。所以,必须保证UITableView的分组,和Cell同时被删除。所以,就需要使用beginUpdates方法和endUpdates方法,将要做的删除操作“包”起来! beginUpdates方法和endUpdates方法是什么呢? 这两个方法,是配合起来使用的,标记了一个tableView的动画块。 分别代表动画的开始和结束。两者成对出现,可以嵌套使用。 一般,在添加、删除、选择tableView中使用,并实现动画效果。 在动画块内,不建议使用reloadData方法,如果使用,会影响动画 一般什么时候使用这么一个动画块呢? 一般在UITableView执行:删除行,插入行,删除分组,插入分组时使用!用来协调UITableView的动画效果。 插入指定的行 在执行该方法时,会对数据源进行访问(分组数据和行数据),并更新可见行。所以,在调用该方法前,应该先更新数据源 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 插入分组到制定位置 插入一个特定的分组。如果,指定的位置上已经存在了分组,那么原来的分组向后移动一个位置。 - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 删除制定位置的分组 删除一个制定位置的分组,其后面的分组向前移动一个位置。 - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation 移动分组 移动原来的分组从一个位置移动到一个新的位置。如果,新位置上若存在某个分组,那这某个分组将会向上(下)移动到临近一个位置。该方法,没有动画参数。会直接移动。并且一次只能移动一个分组。 - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection 在如上方法中,建议使用该动画块进行操作! 问答区 为什么block属性使用copy修饰?答:Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的。ARC和非ARC声明都是一样的,当然注意在非ARC下要release Block。 再一次感谢您花费时间阅读这篇文章! 微博: @Danny_吕昌辉 博客: SuperDanny
Xcode的插件对于开发者来说无疑是开发中的一大利器,让开发者能够将更多的时间和精力放在代码上面。但是开发者都会遇到一个问题,就是每次Xcode一更新,之前好不容易装好的插件全部不能用了,需要重新安装一遍,有时候还会因为某种原因导致即便安装了插件也不能使用的情况。在这里,我将跟大家分享我是如何解决这些问题的。当然,我的方案是建立在前人的基础上完善的。在这里,我先分析插件安装失效的一些原因: 系统安装了不止一个Xcode开发工具 开发者未正确的将自己的DVTPlugInCompatibilityUUID添加到插件中 成功安装了插件,但是却在Xcode识别插件的时候,开发者选择了Skip Bundle这些插件导致 下面,我将把我安装插件的过程以及如何让插件正常使用做一个详细的介绍。下载插件以及编译插件的过程我就不再累赘,想必大家都懂,我就直奔主题了。我们以ColorSense这款插件为例。 流程 首先,我们要先明确我们的插件是要安装到哪个版本的Xcode上面,然后到拿到对应Xcode的DVTPlugInCompatibilityUUID 现在我想在我的Xcode 6.4上面安装这款插件,那么我需要拿到Xcode 6.4的DVTPlugInCompatibilityUUID 右键Xcode,选择显示包内容 然后找到Contents/Info.plist路径下的 Info.plist文件 双击Info.plist文件,找到DVTPlugInCompatibilityUUID,将对应的7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90复制 接下来到ColorSense插件所在目录下 右键插件,选择显示包内容。然后找到Contents/Info.plist路径下的 Info.plist文件 双击Info.plist文件,将刚刚复制的7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90增加到DVTPlugInCompatibilityUUID字段里面。保存 重新打开Xcode 6.4,此时会弹窗提示用户是否加载该插件。很多用户就是在这一步因为选择了Skip Bundle导致不能成功使用插件。 【注意】正确的做法应该是选择Load Bundle选项才对 现在可以Xcode中使用该插件了,是不是很激动!(^__^) 嘻嘻…… 是不是这样就完了?肯定没有,通过上面的步骤,我想大家已经知道了操作流程。那么接下来,讲解一个高效率做法。 终端指令实现 //获取DVTPlugInCompatibilityUUID字段 defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibilityUUID //将XXXX替换成刚刚获取的DVTPlugInCompatibilityUUID find ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins -name Info.plist -maxdepth 3 | xargs -I{} defaults write {} DVTPlugInCompatibilityUUIDs -array-add XXXX 执行这两个指令,即可实现上面繁琐的过程。是不是更加方便? 如果有任何疑问,可以留言,我将尽我所能帮助你。 再一次感谢您花费时间阅读这篇文章! 微博: @Danny_吕昌辉 博客: SuperDanny
Version(应用程序发布版本号) Build(应用程序内部标示) 作用 高效率开发技巧 Version(应用程序发布版本号) 对应的就是CFBundleShortVersionString。该版本的版本号是三个时期分隔的整数组成的字符串:第一个整数代表重大修改的版本,如实现新的功能或重大变化的修订。第二个整数表示的修订,实现较突出的特点。第三个整数代表维护版本。该键的值不同于CFBundleVersion标识。 版本号的管理是一个谨慎的事情,希望各位开发者了解其中的意义。如当前上架版本为1.1.0,之后你更新的时候可以改为1.1.1 Build(应用程序内部标示) 对应的是CFBundleVersion。标识(发布或未发布)的内部版本号。用以记录开发版本的,每次更新的时候都需要比上一次高。如:当前版本是11,下一次就要大于11。比如12,13,etc 作用 比如团队打算发布1.0版本的时候,会发布很多build版本供测试或QA团队进行测试,你发布了很多build,因为一直在修改着代码,因此当你收到一条bug信息时候,你怎么知道是哪个build引起的问题呢,这时候build版本号的优点就可以体现出来了。 高效率开发技巧 在Xcode编译时候自动增加build号码,先把 Info.plist 里的版本号改成某个数字,然后 Targets → your target → Build Phases → Run Script 的地方加上: #!/bin/bash buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE") buildNumber=$(($buildNumber + 1)) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE" 效果如下: 如果没有找到Run Script选项,只需要New一个就行,如下图: 下面这行代码会让Version也自增,一般不需要 /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $version" $PRODUCT_SETTINGS_PATH 另外推荐TraWor写的另外一篇文章浅谈 iOS 版本号 再一次感谢您花费时间阅读这篇文章! 微博: @Danny_吕昌辉 博客: SuperDanny
问题 Homebrew是OS X上类似APT(apt-get)、Yum的一个软件包管理器。所以,虽然你侥幸下载到了brew,但你肯定是无法更新brew的。原因你懂。不过虽然不能更新brew,但这只是指brew这个管理器本身罢了,软件还是可以安装的。但由于无法使用brew update也就无法更新软件。我们有必要来给brew换源。 推荐镜像源 Coding推出了Brew国内镜像源,速度挺快的。 $ cd /usr/local && git remote set-url origin https://git.coding.net/homebrew/homebrew.git $ cd $home && brew update 解决 cd /usr/local //清华镜像源 git remote set-url origin git://mirrors.tuna.tsinghua.edu.cn/homebrew.git //中科大镜像源 git remote set-url origin http://mirrors.ustc.edu.cn/homebrew.git //三者选其一即可更新 如果速度还是很慢,可以尝试以下操作: cd ~/tmp //以下两个选一个(要与你之前选择的镜像源相同) git clone git://mirrors.tuna.tsinghua.edu.cn/homebrew.git git clone http://mirrors.ustc.edu.cn/homebrew.git rm -rf /usr/local/.git rm -rf /usr/local/Library cp -R homebrew/.git /usr/local/ cp -R homebrew/Library /usr/local/ 然后重试brew update 再一次感谢您花费时间阅读这篇文章! 微博: @Danny_吕昌辉 博客: SuperDanny