Showing Posts From

月报

iOS开发月报#13|201907

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。Tips Golbal queues的优先级 如果要在后台执行非UI相关的工作, 一般把这部分工作放在Global queue. Global queue是一种系统内共享的并行的队列。申请Global queue的方法很简单: let userQueue = DispatchQueue.global(qos: .userInitiated)其中后面的.userInitiated参数代表队列的优先级。该优先级公有6中分类,有高到低为: userInteractive>default>unspecified>userInitiated>utility>background 通过该段代码验证: for i in 1...3 { DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { NSLog("DispatchQoS.QoSClass.default, %d", i) } DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async { NSLog("DispatchQoS.QoSClass.background, %d", i) } DispatchQueue.global(qos: DispatchQoS.QoSClass.unspecified).async { NSLog("DispatchQoS.QoSClass.unspecified, %d", i) } DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async { NSLog("DispatchQoS.QoSClass.userInitiated, %d", i) } DispatchQueue.global(qos: DispatchQoS.QoSClass.userInteractive).async { NSLog("DispatchQoS.QoSClass.userInteractive, %d", i) } DispatchQueue.global(qos: DispatchQoS.QoSClass.utility).async { NSLog("DispatchQoS.QoSClass.utility, %d", i) } }其中userInitiated为LIFO(后进先出),即如果有新插入的userInteractive级别的队列任务,为先执行新任务之后再执行之前该级别任务。其余优先级的队列任务均是FIFO(先进先出)。 “No such module” when using @testable in Xcode Unit tests 因为测试工程和主工程分属不同Module,所以如果我们想在测试项目中调用主工程代码需要导入主工程: @testable import moduleName这个时候如果报以上错误,会有以下可能: 1、targetName错误 这个可以去Target->Build Setting->Product Module Name确认。 2、主工程和测试模块支持版本号不一致 保证Build Setting->iOS Deployment Target中的版本号在主工程和测试工程中一致。 authenticating with the app store上传卡顿 如果往AppStoreConnect传包一直卡在这个步骤,可以试一下这种方式: 第一步:cd ~ 第二步:mv .itmstransporter/ .old_itmstransporter/ 第三步:"/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/itms/bin/iTMSTransporter" 等待执行完成。 订阅的freetrial没有标明截止时间被拒 如题,如果订阅功能含freetrial,一定要在freetrial按钮旁边标清楚试用的截止时间。 WakaTime 一款能够统计开发时间的网页端应用,支持众多IDE。集成到IDE之后,注册账号然后会获取到一个Key,绑定该值。然后当我们愉快的开发时,一些开发的信息就会被记录下来。我们可以通过其网页端的dashboard查看我们开发的情况。因为前几天升级XCode,需要重新装插件,所以最近几天的开发记录为空。 Lookin | 免费好用的iOS UI调试软件 出自QMUI团队,可能是小集的影响力已经很大了,这个软件首发之后竟发现已经有一众人开始关注这个东西了。可能因为UI调试这个操作是一个高频的行为,而Xcode目前又做的不够好,所以大家都期待有一个高效的UI调试工具。我也是下下来试了下,结合之前对Reveal和Sherlock的使用,说下自己的体会吧,首先是优点: 1、免费 2、支持动态修改UI元素属性,例如位置,颜色,圆角这些 3、可以查看CALayer的信息 因为是刚发布没多久,还是有些需要改进的地方的 1、动态修改UI元素的信息,均是通过UIView,和CALayer的信息进行修改,无法实现像UILabel改文案,更新行数等操作 2、3D的渲染有时候会有bug出现,位置不正确,另外仅支持一个左右的旋转不支持上下 3、屏幕适配还不支持,不能修改屏幕 其实我对sherlock也都仅限于使用,中和频率不高的使用性和高昂的费用,我选择试用结束就放弃,虽然Lookin还存在一些不足,但是免费大于一切啊,希望可以做的更好,为更多开发者带来便利。 Github R.swift Android开发中引用资源可以通过R机制,所谓R机制就是在我们创建一个Android项目的时候,IDE会自动帮我们创建一个名为R的类型,它所在的文件名称也是叫做R.java,R类型中没有任何方法,包含的是代表不同类型资源的内部静态类,而这些内部静态类中,也只有静态的属性,每个属性代表一个资源,故我们要引用某个资源类型中的某个资源,可用R.资源类型.资源名来引用。下面就是代码中的实例: // 从图片资源文件夹中加载名为"image_test"的图片以其创建位图 Bitmap aBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.image_test);相对来说iOS中的资源引用就麻烦很多,需要通过字符串引用,无法代码补全,资源更换无法自检查。解决iOS资源引用的这些问题而借鉴Android方案的R.Swift应运而生。 传统的方式: let settingsIcon = UIImage(named: "settings-icon") let gradientBackground = UIImage(named: "gradient.jpg")使用R.Swift let settingsIcon = R.image.settingsIcon() let gradientBackground = R.image.gradientJpg()不光图片,它还支持Fonts,Resource files,Colors,Localized strings,Storyboards,Segues,Nibs,Reuseable Cells。 我第一次见到这玩意的时候就一个感受:wocao,🐂🍺! 更多关于R.Swift的使用规则参照官网说明。 Swift-Books github上的内容,可以分为两类,纯干货和干货的整理。这个库属于后者,是一个收录Swift&Object-C相关资料的仓库。收录的都是比较经典的学习资料,而且非常全! 建库已经两年了,但是star却不多,不知道是因为曝光度的问题,还是因为大家对设计版权问题的内容有些警惕。但不管怎样吧,我还是要推一推这个库。 open-source-ios-apps 开源的iOS应用列表。基本涵盖了iOS开发涉及的所有领域,通过完整的app去学习别人的开发技巧是一个相对直观的方式,面对这个大礼包,记得来看一看哈。 Chinese-Podcasts 中文博客的收录,包含科技,风投,生活,电影,设计等领域。 音频内容最大的优势就是便捷,你可以在走路,跑步,甚至休息的时候使用它。音频相比视频还有一个更大的好处是,它会给大脑预留更多的想象空间,听音频我们会思考的更多一些。 去年一直在听东吴同学会,最近一段时间则听ggtalk和硅谷早知道多一些。 文摘 1、生存是一种即时策略游戏,所有的人都是这场游戏的玩家。财务自由了,就是游戏赢家。 --《科技爱好者周刊:66期》2、我以为别人尊重我,是因为我很优秀。慢慢的我明白了,别人尊重我,是别人很优秀;优秀的人更懂得尊重别人,对人恭敬其实是在庄严你自己。3、所谓成熟的人,就是精神上能够自给自足的人。

iOS开发月报#12|201906

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。Tips 闪光动画 图片上的闪光动画,类似这种效果:分析拆解可知这是一个带透明的白色渐变移动产生的效果。渐变+移动,我们可以使用CAGradientLayer + CABasicAnimation实现: func showGradientAnimation() { let gradient = CAGradientLayer() gradient.frame = canvasView.bounds //左上角到右下角的渐变 gradient.startPoint = CGPoint(x: 0, y: 0) gradient.endPoint = CGPoint(x: 1, y: 1) gradient.locations = [0.0, 0.0, 0.0] //调透明度渐变要使用白色 gradient.colors = [UIColor.init(white: 1, alpha: 0.0).cgColor, UIColor.init(white: 1, alpha: 0.3).cgColor, UIColor.init(white: 1, alpha: 0.0).cgColor] canvasView.layer.addSublayer(gradient) let animation = CABasicAnimation.init(keyPath: "locations") //从0位置开始从1位置结束 animation.fromValue = [0, 0, 0.3] animation.toValue = [0.7, 1, 1] animation.duration = gradientAnimationDuration animation.repeatCount = 1 animation.isRemovedOnCompletion = true gradient.add(animation, forKey: nil) }集成Universal Links时的几点注意事项 这个是onlink总结对于各个平台对应用间跳转的支持情况,其中deeplink指Universal Links:跳转至facebook指定页面 如果是facebook某一主页,可以通过: //这里可以替换成自己的主页id let url = URL(string: "fb://page?id=**")! UIApplication.shared.open(url, options: [:], completionHandler: nil)这是使用了app scheme的方式进行跳转的,要求本机安装了facebook才能跳转成功。 如果是跳转到某一个主页的某一个帖子,可以通过: //这里替换成固定的帖子链接 let url = URL(string: "https://www.facebook.com/**/posts/**")! UIApplication.shared.open(url, options: [:], completionHandler: nil)这是通过Universal Links方式跳转的,该链接可以通过苹果的验证。如果未安装应用会跳转到网页端,如果安装了就直接跳到指定页面。 那正常来说,facebook主页也应该可以通过支持deep link的https链接跳转才对,但实际测试来看是不行的。所以如果要加上是否安装的逻辑的话,就是: let url = URL(string: kFacebookHomePageSchemeUrl)! if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { UIApplication.shared.open(URL(string: kFacebookHomePageUrl)!, options: [:], completionHandler: nil) }测试: 推荐:将连接复制到便签,邮件,短信,或者使用二维码的形式使用。 不要将链接粘贴到 Safari 中 - iOS 目前阻止从任何浏览器的地址栏进行深度链接。 验证apple-app-site-association的连接,可以苹果的验证工具 lottie动画效果在安卓和网页端正常在iOS端不正常 在一次使用lottie调用设计提供的动画时,该动画是一个放烟花,然后散开的动画。用网页预览可以完美展示效果,但是放到客户端运行时,却只展示一部分即烟花上飞过程,而没有散开效果。最后仔细查看文档,找到这个:经设计确认,烟花绽放的效果使用了AE中的Repeater(中继器)控件,而该控件在lottie的3.0版本还不支持,但是在2.5.2版本是支持的,回退版本至2.5.2解决了动画确认问题。 PS:一些古怪问题,第一反应要从官网文档说明、Issues、QA中找答案 几个产品相关概念 ROI(return on investment):投资回报率 反应产品的盈利情况,用百分比表示。 市场营销、运营活动,都是企业获利为出发点,通过利润/投资量化目标。利润的计算涉及财务,很多时候用更简单的收入作分子。当运营活动的ROI大于1,说明这个活动是成功的,能赚钱。 ecpm(effective cost per mille):每千次展示可以获得的广告收入 这是广告主预估自身收益的指标。 arpu(Average Revenue Per User):每用户平均收入 ARPU注重的是一个时间段内运营商从每个用户所得到的利润。很明显,高端的用户越多,ARPU越高。 LTV(life time value):生命周期总价值 意为客户终生价值,是公司从用户所有的互动中所得到的全部经济收益的总和。 推荐阅读 23 位开发者告诉你这次 WWDC 最让他们兴奋的新事物 对23位iOS开发者的采访,一起来看下他们眼中这届WWDC什么最让人兴奋。 免费领取小专栏 -- WWDC2019 内参 6月份的WWDC给我们开发者带来了很多东西,Dark Mode、Swift UI、Combine、iPad OS等等。也是我感觉近几年WWDC干货最多的一界了。干货太多不知道怎么学习怎么办?这里没故事的卓同学分享的WWDC2019 内参免费领取名额。可以看各位大佬对WWDC的最新解读分析,目前二十多天里已经更新了30篇文章!。 SwiftUI 的一些初步探索 (一) 这是一篇解读SwiftUI的文章,目前还有第二篇。而喵神也在计划写一本关于SwiftUI 和Combine 的书籍我已经计划写一本关于 SwiftUI 和 Combine 编程的书籍,希望能通过一些实践案例帮助您快速上手 SwiftUI 及 Combine 响应式编程框架,掌握下一代客户端 UI 开发技术。现在这本书已经开始预售,预计能在 10 月左右完成。如果您对此有兴趣,可以查看 ObjC 中国的产品页面了解详情及购买。十分感谢!我发现喵神总能一下找到作为新概念最应该注意的问题,比如SwiftUI和Swift5.1的关系,为什么需要新系统才能预览以及ViewBuilder里接受那些条件语句等。想了解SwiftUI 这篇文章真是必读 Github About-SwiftUIWWDC当天开始建立的一个专门收集SwiftUI资料的仓库,应该是史上最全了。果然大家还是最爱SwiftUI! MovieSwiftUI 使用 SwiftUI & Combine和MovieDB API实现的一款应用。 Talk is cheap, show me the code. 结合实践是最快速的了解一个概念的方式。这也是最近一段时间上升最快的SwiftUI&Combine相关仓库了。 CombineSwiftPlayground 一个帮助理解Combine概念的 Swift Playground。对于首次接触响应式编程的人来说具体事例是帮助理解概念很好的方式。 该Playgrounds要求Xcode11 beta2及以上版本才能查看。 rxswift-to-combine-cheatsheet 列举了Combine和RxSwift之间的差别和一些概念上的对比,对于有一点RxSwift概念的人来说可以快速的理解Combine,也是对于想从RxSwift迁移至Combine的一份参考。 文摘普通选民也开始意识到,民主机制已经不再能够为他们带来权力。世界正在变化,但他们摸不清变化的方式和原因。权力正在转移,但选民不知道权力去了哪儿。在英国选民的想象中,权力被欧盟夺走了,所以他们投票脱欧。而在美国选民的想象中,是既得利益者垄断了权力,所以他们支持反体制的候选人,比如伯尼·桑德斯和唐纳德·特朗普。但可悲的事实是,没有人知道所有的权力去了哪儿。就算英国离开欧盟、特朗普接掌白宫,权力也绝不会回到普通选民身上。 --未来简史月度小结 关于WWDC WWDC发布的信息里面最让我感兴趣的就是SwiftUI和Combine,目前从Github的活跃情况来看也是这两个技术相关仓亏最多。SwiftUI解决了写UI布局的痛点,而Combine的出现表明了苹果对响应式编程的认可,对于我这种使用了一年RxSwift的人来说简直是一种福音。还有一点是这两个大招都是对Swift的支持,这是一个很明显的信号:Swift已经很成熟了,很强大了,以后还会有更多更好的优化。 过去几年,你可以忽视 Swift,但是,未来几年,如果不拥抱 Swift,将无法紧跟着 Apple 生态圈。 关于生活 从上个月开始有规律的进行跑步,上个月跑步里程是60km,这个月是70km,距年初定的目标已经完成了56%。 跑步带来的一个好处是最近一段时间的睡眠质量有显著提高,睡得更香了。如果你有睡眠质量问题,可以考虑跑步这个方式尝试解决哦。 封面图来自于在公司园区跑道上的拍摄。

iOS开发月报#11|201905

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。Tips 对UISearchBar样式的修改 1、完成一次搜索之后,调用 searchBar.resignFirstResponder()隐藏键盘,会将 searchBar的取消按钮默认置为disEnable。如果我们需要此时能够监听取消按钮的点击状态,需要恢复其可用状态: if let cancelButton = searchBar.value(forKey: "cancelButton") as? UIButton cancelButton.isEnabled = true }2、修改取消按钮的文案 //修改searchbar的取消按钮文案 searchBar.setValue("delete", forKey: "cancelButtonText")3、更改取消按钮文案样式 UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]) .setTitleTextAttributes([NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)], for: .normal)4、更改searchBar文本框文字样式 UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]) .defaultTextAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]上传IAP时出现TCP 443问题 具体错误为: Communication error. Please use diagnostic mode to check connectivity. You need to have outbound access to TCP port 443.这是由于代理问题引起的上传错误,上传IAP至App Store Connect不需要代理,关掉代理继续上传就可以了。 上传IAP时出现:WARNING ITMS-90176 完整错误为:WARNING ITMS-90176: "Unrecognized Locale - The locale names used in localization directories at ( "Payload/sandbox.app/AccountKitStrings.bundle/Resources/cb_IQ.lproj" ) are invalid. iTunes supports BCP47 but not the UN M.49 specification. Refer to the Language and Locale Designations guide at https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html for more information on naming your language-specific directories.”该错误是由Xcode9不再兼容cb_IQ.lproj这个格式,所以只要一出AccountKitStrings.bundle中的cb_IQ.lproj文件即可。 推荐阅读 Core Image 之自定义 Filter~ 非常详细的介绍Core Image中Filter(滤镜)涉及的概念和使用方法。 作者是美图的iOS开发工程师,博客多讲解图像处理相关知识,是iOS图像领域的大牛,推荐关注。 iOS图形处理概论:OpenGL ES,Metal,Core Graphics,Core Image,GPUImage,OpenCV等 对于刚接触iOS图形相关框架的小白,有一些图形框架在字面上和功能上非常容易混淆。这里旨在总结一下各种框架,区分它们的概念和功能,以作日后进一步细分学习的指引。 Swift 5 字符串插值之美 Swift5除了ABI稳定并没有带来很多语法上的变化,你如果以为Swift5只有ABI稳定那你就错了,它还带来了一个很强的特性---字符串插值。一开始我还以为它是一个小特性,但是当我把它和AttributedStrings, sql联系到一块时,我才发现他的强大之处! 还有一篇将字符串插值应用到AttributedStrings上的文章: StringInterpolation in Swift 5 — AttributedStrings 如何选择开源许可证 关于几种常见开源许可证的区别:Github Python-100-Days Python - 100天从新手到大师。 作为一线移动端开发,或多或少都有着一个全栈的目标,而作为后端语言的Python无疑是最佳选择。一个star数多达3w+的Python教学项目,这足以说明Python的受欢迎程度,这份教程的受欢迎程度。 markdown-weixin 一个在线将 Markdown 转换为微信公众帐号文章格式的工具。 http://md.qikqiak.com/ AssetsExtractor 『Assets提取工具』是一款OSX平台上用于将Assets.car或xxx.app中打包的png图片、pdf等资源重新提取出来的开发者工具。Assets.car常见于iOS/Mac/Unity等开发中的资源打包。 awesome-ios-bluetooth 一个收集开发iOS蓝牙功能资料的仓库。从入门必读、蓝牙升级、ANCS、调试工具都有介绍。目前该库由我维护,大家有什么蓝牙相关的问题可以提issue给我。 firefox-ios 火狐浏览器的iOS开源库,由Swift4.2编写。是一个优秀的可供参考学习的开源项目。

iOS开发月报#10|201904

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。Tips 关于分享中的一些问题 微博多媒体内容的缩略图,即thumbnialData的大小应小于32K。否则会导致分享失败 微博分享的AppStore下载链接无法打开,这是因为微博屏蔽了指向AppStore的链接。一个可行的做法的将微博的下载链接增加一个引导页,提醒用户通过Safari打开。微信多媒体缩略图不能超过64K。 微信分享从6.7.2之后无法获知是否真的分享成功。这个是官方的调整,旨在减少“强制分享至不同群”等滥用分享能力。facebook 关于facebook分享的采坑可以查看这篇文章,基本也都是我遇到的问题。iOS Facebook 分享中的坑 Twitter 相同内容分享两次之后再分享将失败。 SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2. (in target 'SwiftyJSON’) 这个问题是多人写作开发引起的,有一个同事将SwiftJSON的版本升级至4.3.0,使其支持Swift5.0,但是Swift5.0跟Swift4.0+不兼容,导致出现上面的错误提示。 修复方式,手动指定SwiftJSON版本,使其低于或者等于4.2.0。 pod 'SwiftyJSON', '~> 4.2.0'Encountered an unknown error (Unable to find a specification for FrameworkA depended upon by FrameworkB FrameworkA和FrameworkB都是私有的Cocoapods库,在制作FrameworkA时引用了FrameworkB,如果执行pod spec lint就会出现如上的提示,到时lint无法通过。 这是因为lint在对引用库验证时,默认只验证官网的仓库,我们需要手动添加验证源才能通过,方法是: pod spec lint --sources=git@bitbucket.org:company/privateRepo.git,https://github.com/CocoaPods/Specs --allow-warningsTabbar的初始化会立即调用viewDidLoad方法 正常当我们初始一个UIViewController的时候,总是先执行init方法,执行完之后才会调用viewDidLoad方法。但是如果是UITabbarViewController的初始化则不同,它会在执行init方法的时候立即调用viewDidLoad。 来自Stack Overflow的解释: UITabBarControllers call loadView inside [super init] method, which causes the call to viewDidLoad. So the viewDidLoad method will be called before init has finished its job.If you have some thing to setup in viewDidLoad you should perhaps do it inside init method after the call to [super init].AppleScript打印换行 当我使用AppleScript编辑一段脚本时,有一个需求是打印一段换行的文本,试了很多方案,包括\n,\r, ASCII码等都不行,脚本执行时会忽略\符合自动换行,分开执行输入内容。 最后在不断尝试中找到了一种方案,直接敲出换行,如下,在变量_input后拼接一个换行符: tell note1 to append text "[*]" & _input & " "由于第三方SDK使用了用于定位的功能,导致收到苹果的隐私警告邮件后来定位的到的原因是Facebook相关的几个库均使用了CoreLocation,也就是说Facebook有偷偷使用用户地理位置权限的嫌疑。当然联系他们提供不带此功能的SDK也不显示,后来在react的issuse中也发现了对于此问题的讨论。#20879 解决方案如下,即增加对应的隐私条款选项:违反Guideline2.3.1被打回在一次小版本提交时,遇到了这个违反Guideline2.3.1被打回的问题,也是第一次遇到。原因描述是含有模糊代码,选择器错误或者误导审核的功能。实际排查中我们并未猜想到哪里可能导致这些问题。 后来通过邮件跟审核人员沟通,得到如下回复:问题又好了?猜想可能是苹果审核的问题,他们也是会犯错误的。 后来在网上查过这种问题的处理情况,从资料时间来看,可以确定这个是最近一年才出现过的问题。处理方法是,首先是应该跟审核人员联系,让他们告知是哪里的问题;然后再根据情况进行修改。 推荐阅读 作为面试官,哪类 iOS 开发更容易被你青睐? 来自老司机团队的文章,正在找工作的你肯定能从中有所收获。 再贴一个我司的招聘信息,有意愿的小伙伴赶紧简历投过来。 用户端智能的应用实践 bang写的关于应用功能优化的几个点,根据用户的特征推荐金额,根据用户使用习惯调整push的时间,有些方案不一定能够为我们所用但是解决问题的思路还是挺不错的。 谈Linux,Windows,和Mac 王垠的博客其实已经被删除了,这个是网络留存的快照文件。牛B的人总是能留下牛B的话,这是王垠关于Linux、Windows和Mac的看法。 Swift语言的设计错误 let shoppingList = ["Eggs", "Milk"] //这么写应不应该报错? shoppingList[0] = "Salad"关于以上问题的讨论。当前Swift是不允许对let生命的数字修改内部元素的。在王垠看来这是一个低级的错误,具体为什么这么定义“不对”,可以看文章讨论。 音视频 Github iOS-Source-Probe iOS源码探求系列,是iOS相关源码的分析合集。 AutoInch 优雅的iPhone全尺寸/等比例精准适配工具,可以通过设置一个屏幕的尺寸值,而自动配置其他尺寸值。支持代码和xib两种方式。SwiftTips 作者总结的Swift使用中的一些小技巧,和喵神的《100 个 Swift 必备 tips》有异曲同工之妙。作为一名刚入门的Swifter,这两个资料都是非常推荐看一看的。 ZFPlayer 非常全面的一款iOS播放器,支持AVPlayer和ijkplayer两种播放模块。项目包含主流的视频播放场景,抖音、微博短视频等样式。如果是对视频功能有需求的小伙伴非常建议看一看。

iOS开发月报#9|201903

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。Tips Spine + SpriteKit 项目中需要引入一些实物动画,每个动画之间有不同形态的切换,考虑过gif, mp4, AE + lottie, Spine + SpriteKit。 最后确定使用Spine做动画效果,用SpriteKit处理动画。Spin并没有官方支持SpirteKit的库,但有一个做的比较好的第三方库maxgribov/Spine,支持Swift4.1。 该库支持Bones, Slots, Skins等常用的动画要素,通过Spine导出的json文件和动画素材做出各种动画效果,是仅有的近期还在维护的支持SpriteKit的Spine运行时库。但它也存在一个问题,还不支持Mesh Animation(网格动画)。如果所需的动画效果不需要网格的话,非常推荐使用这个库。 而我们所需的动画效果又必须用到网格动画,思考再三考虑决定放弃使用这个库,使用SKTextureAtlas(纹理集) + 逐帧动画来实现特殊的动画效果。虽然输出的还是png序列,但是SpriteKit对纹理集有足够的优化,Xcode会在打包时把.atlas文件夹中的所有图片做成一张合图,然后生成一个plist文件描述每个小图片的位置信息,所以包的大小和渲染成本都会大大降低。 快速创建转场样式 说到自定义转场我们可能会直接想到UIViewControllerAnimatedTransitioning,结合这个类我们可以实现多种多样的订阅样式。但是使用这种方式做转场,我们需要引入很多代码。有一种简单的实现转场的方式是通过CATransitionAn object that provides an animated transition between a layer's states.通过文档的介绍我们知道,这个类就是用来做转场的,只不过支持的样式有限,但如果正好满足你需要的话,推荐使用这种方式来实现转场。 我们来实现一个present的渐变效果: //创建transition对象 let transition = CATransition() transition.duration = 0.5 //动画样式 //type: .fade, .moveIn, .push, .reveal transition.type = .fade //动画出现方位 //subtype: .fromRight, .fromLeft, .fromTop, .fromBottem //transition.subtype = .fromRight transition.timingFunction = CAMediaTimingFunction(name: .easeIn)self.view.window?.layer.add(transition, forKey: "present") self.present(targetVc, animated: false, completion: completion)如果是做push的渐变,我们只需要改变最后的控制动画的代码: //父容器为UINavigationController self.view.layer.add(transition, forKey: "push") self.present(targetVc, animated: false, completion: completion)//父容器为UITabbarController self.tabBarController?.view.layer.add(transition, forKey: "push") self.present(targetVc, animated: false, completion: completion)什么是UserAgent User-Agent 首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号,然后前端的展示就可以根据这些信息进行针对性的优化。 我们打开Chrome浏览器,生成一个请求,然后用Charles抓包,可以看到对应的User-Agent user-agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.28 Safari/537.36通过UA我们可以得到以下信息:信息项 内容浏览器名称 Chrome浏览器版本号 70.4.3729.28渲染引擎 WebKit 537.36操作系统 Mac OS 10.13.6Apple Configurator 2 出现 Unauthorized Error 注销账号,再次登录 Command PhaseScriptExecution failed with a nonzero exit code 运行一个项目时遇到了这个bug提示,一直编译不过去,这其实是一个Xcode10引起的bug。 解决方案: 在Xcode菜单栏选择File -> Workspace Setting -> Build System 选择Legacy Build System 重新运行即可。 参考:踩坑Xcode 10之New Build System 斐波那契函数 斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、…… 这个是我们公司技术面试的必问题目,也是筛掉人数最多的一个问题。有一部分同学会使用数组尝试解决这个问题,但这会把问题复杂度升级,还有些可能根本没有思路。但其实这个问题不复杂的,用到递归可以很快的解决。斐波那契函数的数学表达是: F(1)=1 F(2)=1 F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)用Swift实现就是: func fibonacci(n: Int) -> Int { if n == 1 || n == 2 { return 1 } else { return fibonacci(n: n - 1) + fibonacci(n: n - 2) } }推荐阅读 SpriteKit Tutorial for Beginners raywenderlich上介绍SpriteKit入门的一篇教程,通过这篇文章你可以实现一个忍者击杀怪物的小游戏,理解SpriteKit框架里常用的几种游戏元素。不得不说这个教程做的是真的棒👍 开发小知识 该文章主要整理一些小知识点,主要涉及 iOS 以及计算基础相关知识点,某些知识点暂时只有标题,后续会持续更新。笔者最近一段时间面试过程中发现一些普遍现象,对于一些很不起眼的问题,很多开发者都只停留在知道、听说过的层面,但是一旦问 是什么 和 为什么 ,很多应试者回答的并不理想。 大家可以对着这篇文章查找自己的知识盲区。 Swift 5 终于来了,快来看看有什么更新!! Xcode10.2 已经发布,是时候开始使用 Swift5 了,可以提前看下老司机周报总结的Swift5 更新内容,对适配工作做好准备。 苹果开了一场没有任何硬件的发布会 3 月 26 日凌晨 1 点,苹果在 Apple Park 新总部的乔布斯剧院召开了春季特别活动。 在活动现场,苹果发布了:新闻服务 Apple News+ 可以返现的 Apple Card 游戏服务 Apple Arcade 全新的 Apple TV App 服务 Apple TV+ 原创视频服务全部是软件和服务,没有新硬件的出现——这或许意味着,苹果正在寻找下一个十年的生长空间。 音视频 辞职环游中国的程序员小 K 大概每个人都有过这种冲动,辞掉工作出去旅行,想去哪就去哪,再也不用赶需求修 bug 通宵加班。不过对大多数人来说,也就止步于“想想”,并不会付诸行动。但是我身边有一位朋友,真的做到了这件事:辞职一年环游中国! 这是最近几期ggtalk对我触动最大的一期,同样是做iOS开发的,为什么人家那么优秀🙃 Github 996.ICU 工作996,生病ICU。这段时间的“明星项目”,旨在反抗国内互联网公司形成的每周工作6天,每天工作早9点到晚9点的不良加班风气。截止到3月30号,仅四天时间star已经超11万。 XVim2 XVim2是一个用于Xcode的Vim插件。如果你是一个Vim党,你可以直接在Xcode代码编辑界面使用Vim的各种特性。我是最近开始接触,也在慢慢适应Vim的远离鼠标工作模式。另附送一个安装流程: 1、关闭Xcode 2、钥匙串->证书助理->证书创建 名称:XcodeSigner 身份类型:自签名根证书 证书类型:代码签名 3、重新签署Xcode #需要等待一段时间 sudo codesign -f -s XcodeSigner /Applications/Xcode.app 4、按照官方步骤安装XVim2 文摘 1、培养出在没人监督自己的时候也能高效工作的自我责任感非常重要。你也可以拔这称为是具有一种性格或者具有一种素质,它们都是同一个概念。如果缺乏对自己的责任感,你将永远依赖外部动机来驱使你努力工作。你容易折服于一根胡萝卜的诱惑,也容易屈从于一根大棒的威胁。 --《软技能:代码之外的生存指南》 2、最终,它成为我自己的知识体系中严重的短板。没有花时间去彻底掌握Lambda表达式的工作原理,结果浪费了大把的时间。最后当我下决心花时间去了解Lambda表达式的时候,我只花了几个小时阅读并实践,就领会了这一概念。 --《软技能:代码之外的生存指南》 3、回到从前,在我们刚开始一起生活的时候,我们就决定将我们收入的10%用于风险什一税--实际上我们把这部分收入捐给一家慈善机构,以帮助印度的孤儿。在我们第一次奉献什一税的第二周,我的妻子就得到了加薪,加薪的数额正好是我们当时奉献什一税的数额。我个人认为,我们的成功很大一部分就是因为这种对奉献的承诺,一直恪守到今天。 即使你不信仰任何宗教,我认为这一点也有某种符合逻辑的解释。我认为,你把钱看的越重,你就越难以在理财方面做出明智的、成功的投资选择。自愿把自己收入的固定数额奉献或者捐赠给慈善机构,可以改变你对金钱的看法。这一思想上的转变让你从金钱的所有者变成管理者。 --《软技能:代码之外的生存指南》

iOS开发月报#8|201902

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。新闻一览 2019年开发人员技能报告 这是HackerRank平台通过对7万多名开发人员做的一项调查,问题从他们认为最有前途的技术到他们在工作中和面试过程中想要的是什么,甚至还有coding过程中听什么音乐最有帮助!一起看下吧。 Tips presentedviewcontroller和presentingViewController 假设从A控制器通过present的方式跳转到了B控制器,那么 A.presentedViewController 就是B控制器; B.presentingViewController 就是A控制器。如果没有对应关系会返回nil 获取类名 //传入实例对象object let typeName = String(describing: type(of: object))隐藏NavigationBar导致手势返回失效 当我们从一个A(NavigationController)界面通过push进入B界面,如果要隐藏B的NavigationBar,那么从B到A的返回手势也会被屏蔽掉。 如果需要加回来我们可以这么做: //1.给需要添加返回手势的ViewController增加UIGestureRecognizerDelegate//2.由当前ViewController接管pop手势 self.navigationController?.interactivePopGestureRecognizer?.delegate = self关于 Decimal 官方对Decimal介绍就一句话A structure representing a base-10 number.表示以10为基数的数的结构体,就是十进制的表示方法。 它可以通过 mantissa * 10 ^ exponent的形式表示任意数值。mantissa(尾数)是最长38位的整数,exponent(指数)是 -128到127的整数。 你也可以把Decimal理解成跟Int, Float, Double一样的数据类型,那有了整形,浮点型数值还会出现Decimal这个类型? 可以看一个例子: let stride: Float = 0.01 var sum: Float = 0 for _ in 1...100 { sum += stride } print(sum)//0.99999934sum的值不是1,这是因为精度的问题,那如果把Float换成Decimal就会完全等于1。 SKProduct的price属性就是Decimal类型 违反5.5条款被打回的经历 在一次有关内购项审核被打回的经历中,遇到了一个新问题,错误描述中提示违反了条款5.5 Developer Code of Conduct。 简单看下这一条Please treat everyone with respect, whether in your responses to App Store reviews, customer support requests, or when communicating with Apple, including your responses in Resolution Center. Do not engage in harassment of any kind, discriminatory practices, intimidation, bullying, and don’t encourage others to engage in any of the above. Customer trust is the cornerstone of the App Store’s success. Apps should never prey on users or attempt to rip-off customers, trick them into making unwanted purchases, force them to share unnecessary data, raise prices in a tricky manner, charge for features or content that are not delivered, or engage in any other manipulative practices within or outside of the app.总结一下就是要尊重用户。 在联系过苹果审核客服之后,被告知是因为在引导过程中弹了两次订阅弹窗(一次订阅,一次freetrial)被拒的。但其实这个流程已经跑了大半年,一直没问题,这次却突然被揪出来了,有点莫名其妙。 可能审核人员认为,这种行为有诱导或者强迫用户付费的嫌疑。由此也可以看出审核人员对项目流程的管理越来越严格了。去掉一个步骤之后再次提交就过审了。 推荐阅读 Swift ABI 稳定对我们到底意味着什么 Swift 社区最近最重大的新闻应该就是 ABI 稳定了。这个话题虽然已经讨论了有一阵子了,但随着 Xcode 10.2 beta 的迭代和 Swift 5 的 release 被提上日程,最终 Swift ABI 稳定能做到什么程度,我们开发者能做些什么,需要做些什么,就变成了一个重要的话题。 一起来看下使用 Swift 5 会对我们的项目有什么影响以及怎么处理。 Void SwiftGG翻译组翻译的Mattt一篇介绍Void的文章。 Void是什么,它和nil有什么区别,这个我们经常用但不一定真的理解的东西,一起通过这篇文章看下吧。 love2.io 程序员的自我修养,作为一个合格的程序员需要具备哪些知识呢? 数据结构、算法、代码架构、项目管理工具、网络知识等?可能还不够,还需要懂点设计、学着当一个合格的PM、学会团队合作、学会学习、提升效率等等;够了吗?你还需要了解一下创业流程、关注自己的身体健康。。。 太多了,不列举了,因为这里都有。 为什么数字下标要从 0 开始而不是 1? 大家都知道数字下标是从0开始的,那为什么是0而不是1呢?说实话在看这篇文章之前我也不能很清楚的说明白,如果你也有这种疑惑就看看这个文章吧。 什么叫有知识 选自罗胖60秒: 1、话说什么叫做有知识?最近我看万维钢老师有一个有趣的定义。他说,考试得了高分,不叫有知识。茶余饭后能高谈阔论,这也不叫有知识。 你发现没有,这些场合下,知识虽然有用,但是这些知识都不太牵扯到具体的得失,所以只是智力游戏。 2、那什么才叫有知识呢?万维钢老师说,只有当局势不明朗,没有人告诉你该怎么办,而错误的判断又会导致一些不良后果,这个时候,如果你因为有知识而敢于拿一个主意,你才算是真有知识。 3、请注意,这不是在说,实用的知识才是知识,而是在说,只有当知识能够帮助你做实际决策的时候,它才是你的知识。 4、这种决策当然可大可小,大到你选择什么样的职业,选择听医生的哪个建议;小到帮你选择看哪一部电影,或者是为孩子挑选买哪一本书。这都是具体的决策。 音视频 苏俄科幻简史:直指星海的冒险时代 最近在机核(gamecores)上听到的一段讲述苏俄科幻的音频,可以当故事听,也可以当一段历史去听,非常有意思。 对于很多人而言,苏俄的科幻文学、电影乃至游戏都是一个比较陌生的概念。事实上从沙俄末期到苏联再到今天的俄罗斯,来自极北之地的斯拉夫人民也曾在科幻这个领域上走了很远很远,取得了惊人的成绩。 Github DoraemonKit 由滴滴团队开源,中文名“哆啦A梦”。 DoraemonKit 是一个功能集合面板,能够让每一个 App 快速接入一些常用的或者你没有实现的一些辅助开发工具、测试效率工具、视觉辅助工具,而且能够完美在 Doraemon 面板中接入你已经实现的与业务紧密耦合的一些非通有的辅助工具,功能强大,接入方便,便于扩展。 git-history 这个项目可以让你在浏览器动态地查看Github库上代码的变动历史。 使用方法非常简单: 1、在对应的文件url中把github.com替换成github.githistory.xyz 2、没有第二步 resume.github.com 这个库可以通过你仓库的活跃事件来生成一份在线简历。使用很简单,直接访问以下网址:https://resume.github.io/?yourusername 需要将yourusername替换成你的用户名,别忘了那个? iOS-Developer-Roadmap 一名iOS开发人员需要具备哪些知识呢?这个库做了比较详尽的总结,以技能树的形式对iOS开发所需知识点,知识面做了整理,我们可以作为参照,查看自己有哪些缺失。文本版本 图片版本(基础版本, 完整版本)

iOS开发月报#7|201901

这里记录过去一个月,我看到的值得分享的内容,包含但不限于iOS知识,每个月的最后一天发布。 欢迎推荐内容,可以前往zhangferry/iOSMonthlyReport提交issue。 另外,马上就要过年了,提前祝大家春节快乐!新闻一览 Github宣布私有库免费 Github宣布提供免费且不限量的私有仓库服务,如果协作者超过3个仍需购买付费服务。 喜大普奔啊,得知消息之后,我赶紧就建立一个私有仓库,放一些不想让你们看到的东西🙃 Swift 5 Release Notes for Xcode 10.2 beta Swift5 版本终于发布了,最低可以使用Xcode10.2beta 查看。Swift5 带来了ABI稳定,届时系统将自带Swift动态库,由Swift编译的项目将不会自动把运行库打到包里。 Swift5 具体还有哪些特性,可以参考知识小集团队的翻译版本 Tips Dictionary的merge操作 Dictionary的merge可能平常用的不多,我是在一次bug排查的过程中,发现自己对merge操作理解也有些偏差。这里做个简单梳理。 看其中一种情况: var dictionary = ["a": 1, "b": 2]// Keeping existing value for key "a": dictionary.merge(["a": 3, "c": 4]) { (current, _) in current } // ["b": 2, "a": 1, "c": 4]// Taking the new value for key "a": dictionary.merge(["a": 5, "d": 6]) { (_, new) in new } // ["b": 2, "a": 5, "c": 4, "d": 6]注意尾随闭包中有两个参数,返回第一个参数,表示如果merge操作有重复key值,将保留merge前的value不变。如果闭包返回第二个参数,如果merge有重复key,将更新至最新值。 Dictionary向关的merge函数一共有四个,使用类似: public mutating func merge<S>(_ other: S, uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows where S : Sequence, S.Element == (Key, Value) //上面示例对应方法 public mutating func merge(_ other: [[Key : Value].Key : [Key : Value].Value], uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrowspublic func merging<S>(_ other: S, uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows -> [[Key : Value].Key : [Key : Value].Value] where S : Sequence, S.Element == (Key, Value)public func merging(_ other: [[Key : Value].Key : [Key : Value].Value], uniquingKeysWith combine: ([Key : Value].Value, [Key : Value].Value) throws -> [Key : Value].Value) rethrows -> [[Key : Value].Key : [Key : Value].Value]一些Git操作 删除子目录git 当一个含git的目录包含了其他含git目录时,外部git是不能将内部git纳入版本管理的。有一种解决方案是,移除内部的git仓库。 1、删除内部git cd 需要移除的git目录 rm -rf .git # 或者显示隐藏目录手动删除.git文件夹此时虽然已经删除了.git但是原目录还存在缓存,无法添加到外部git中。 2、删除缓存内容 git rm -r --cached nextgit pull强制覆盖本地文件 当我们需要将某一分支,跟远程对应分支保持一致时,可以做如下操作: git fetch git reset --hard origin/branch_name git pullfatal:refusing to merge unrelated histories 当我们为一个项目关联了一个远程仓库,执行pull的操作时出现 * branch master -> FETCH_HEAD * [new branch] master -> origin/master fatal: refusing to merge unrelated histories这是因为git从2.9.0版本开始,预设行为不允许合并没有共同祖先的分支。如果你非要合并,需要加上--allow-unrelated-histories参数才行: git pull origin master --allow-unrelated-histories几个提高效率的快捷键 最近在熟练使用了几个快捷键后,明显感觉开发效率提高了不少,这里总结了几个常用但不是非常常用的快捷键(Command + C 和 Command V等)。另外Command(或 Cmd)⌘ Shift ⇧ Option(或 Alt)⌥ Control(或 Ctrl)⌃ Power 电源键Xcode Command + Shift + J 文件目录指向当前文件 Command + Shift + O 文件、对象搜索 Command + Shift + F 全局搜索 Command + shift + , 编辑 scheme Command + Control + Left/Right 浏览历史切换 Command + Option + Left/Right 折叠、展开当前代码段 Command + Option + C 显示Commit界面 系统操作 Command + Option + Power 睡眠状态 Power 1.5s睡眠状态 Command + Control + Power 强制Mac重启 适用其他程序 Command + , 打开偏好设置 Command + W 关闭最前面的窗口 Command + Option + W 关闭应用所有窗口 Command + Q 推出应用 Command + M 最小化 Command + Shift + [/] 左右切换应用tab 推荐阅读 未来世界的幸存者 今年市场经济不景气,很多互联网公司经历“裁员潮”,其实不光是换联网行业,其他行业也面临同样的问题。那怎么提高自己的核心竞争力,是每一个从事互联网行业的人都应该考虑的。我们不应该只满足于日常开发,要找准自己的定位,提早做职业规划。另外不断尝试拓展自己的知识面,增加自己的影响力,这样才能让自己在不断变化的市场中立于不败之地。 除了阮老师的这本书,还推荐 卓同学的iOS 2019 隆中对 什么是真正的程序员 作者仿照《小王子》中的情节,通过小printf遇见的不同类型的程序员,最后悟出什么才是真正的程序员! 有两个概念比较有意思:达克效应:能力强的人总是低估自己,能力弱的人总是高估自己。 能够为人们解决真正需要解决的问题的程序员,才是真正的程序员Language Server Protocol 来自SwiftGG翻译组的一篇文章。 苹果公司 在 Swift.org 论坛上宣布,正在着手为 Swift 和 C 语言支持 Language Server Protocol(语言服务器协议,LSP)。 那这意味着什么呢?这可能是苹果自 2014 年将 Swift 作为开源软件发布以来,为 Swift 做出的最重要的决定。它意味着我们可以不必使用Xcode去开发Swift项目,而是可以选择像是Visual Studio, Atom这些IDE。Swift将变成一种更加通用的变成语言。 Hashable / Hasher Hash是程序开发过程中很重要的一个概念,它可以让我们查找集合特定元素的时间复杂度由O(n)降低到O(1)。 这篇文章介绍了Swift中关于哈希的实现,哈希冲突的改变,以及Swift4.1之后关于自动化实现Equatable的相关介绍。 Table View 太複雜?利用 MVVM 和 Protocol 就可以為它重構瘦身! TableView是我们常用的系统部件,随着业务逻辑的不断迭代它内部的逻辑可能会越来越复杂,代码量也越来越多,如果不能很好的组织逻辑架构,这里会变得非常臃肿。 作者利用MVVM pattern,加上一点Protocol技巧,来简化dataSource的工作,把UI和逻辑解耦合,并且最大化这个tableView模组的扩展性。 音视频 临近年关,可能很多人会考虑年后换工作的情况,最近几期的ggtalk也在讨论跳槽这个大家比较关注的话题,希望大家收听之后能够有所收获: 狭义跳槽论:面试官,大厂新人和准备起跳的某 C 广义跳槽论:简洁明了的方法论 附带之前的几期关于升职加薪的节目: 聊聊程序员的升职加薪(上) 聊聊程序员的升职加薪(下) 程序员的春节趣事 今天上班路上听了这期节目,全程非常的欢快。我是今天晚上回家的火车,那些相似的春节趣事我也马上要经历一次了,是需要提前准备下怎么应对了🙃。 以下是官方内容介绍: 快过年了,估计大家也没心情工作学习,准备进入放假状态。严肃的话题放到年后,这期我们请到了袁滚滚、张思琦和老朋友莲叔,围绕着春节轻松愉快的聊一期。 Github 本篇GitHub模块,介绍几个Git和GitHub相关的项目。 Git中文教程 来自geeeeeeeeek的高质量Git中文教程,在GitHub已经获得了1w+的start。对git操作还有疑问的同学可以借助这份教程,完整的梳理一下git相关的知识,或者扫一下git盲区。 Git的奇技淫巧 我们在使用git的时候,有时候会被一些具体的问题难住,这篇文章从具体操作入手,介绍一些我们知道但不一定都用过的git指令。 等等,我也想写一份我自己的Git奇淫技巧了,因为git这个东西是真的庞大。 HelloGitHub 这是一个面向编程新手、热爱编程、对开源社区感兴趣人群的项目,内容以月刊的形式更新发布。内容包括:流行项目、入门级项目、让生活变得更美好的工具、书籍、学习心得笔记、企业级项目等,这些开源项目大多都是非常容易上手、很 Cool,能够让你用很短时间感受到编程的魅力和便捷。从而让大家感受到编程的乐趣,动手开始编程。 GitHub漫游指南 介绍你可能在Github上遇到的所有问题,从如何起一个好名字到如何推广项目,GitHub用户分析,每一个环节都非常详细。无论你处于编程的哪个阶段,我认为你都能从这份指南中获取到灵感和帮助。 README 该文件用来测试和展示书写README的各种markdown语法。GitHub的markdown语法在标准的markdown语法基础上做了扩充,称之为GitHub Flavored Markdown。简称GFM,GFM在GitHub上有广泛应用,除了README文件外,issues和wiki均支持markdown语法。

iOS开发月报#6|201812

大事件 这个月的大事件是高通与苹果之间的专利大战。高通分别在中国和德国对苹果公司提起专利诉讼,结果均胜。苹果将在中国禁售iPhoneX及之前的机型。在德国则是全面禁售。加上新品手机的创新力度不够,导致苹果股票持续走低,四个月前,苹果还是首个突破万亿美元市值的上市公司,到现在股价已经下跌了 36%,市值只有 7160 亿美元,几个月内市值损失接近 3000 亿美元。Tips 更换启动图不生效 现象:更换启动图,不生效,即使删除旧图,运行程序启动图仍然是之前的版本。 尝试了删除app重装,clean、重启Xcode,删除DriverData,还试过挪图片位置,改名字,均无效。最后尝试了重启手机->再安装才正常。 问题分析:这个是系统问题,为了加快程序启动的速度系统会把启动图做个缓存,之后每次启动是加载缓存启动图,所以才会出现修改不生效问题。但我们也并不知道系统会什么时候更新启动图,这个就有点尴尬😓 stackoverflow上有关于这个问题的讨论: iOS Keeping old launch screen and app icon after update 有人说这个不影响线上版本,从上线之后的测试来看,大部分用户没有受到影响,但还是出现了部分手机升级却未更换启动图的问题。 因为是系统层面控制,没有太好的解决方案,如果有人遇到这个情况,需要注意一下。 赋值权限 在Mac 系统的终端上修改文件权限使用的是 Linux 中的 chmod 命令: r:可读,表示可以读取内容 w:可写,表示可以编辑内容,但是不可以删除文件 x: 可执行,表示可以执行文件。 r:4 w:2 x:1 rwx=4+2+1=7$ chmod 777 file这行命令的意思就是:文件所有者权限是7,同用户组的权限是7,其他非本地用户组的权限是7。同理可以出现这样的命令 chmod740.barshrc 再讲两个: chmod u=rwx, go=rx .barshrc 这个命令中u表示拥有者,g表示group中的用户,o表示others,和上面的想对应。注意go=rx,这里go是拼接起来的,表示g和o的用户有读和执行的权限。 $ chmod a+w .barshrc表示所有的用户(a表示all)增加写的权限。 App图片瘦身 随着项目版本的不断迭代,会很容易积累越来越多的图片,对这些图片我们有两个主要方面可以优化: 1、删除不用的图片 我们需要借助一个工具LSUnusedResources 它可以检索项目中未使用的图片资源,我们可以指定检索图片的路径,后缀进行过滤。 注意:无法区分png序列的引用。最好手动删除 2、图片压缩 可以通过ImageOptim,对图片进行无损压缩。使用时只需将要压缩的文件拖到程序中即可。 如果图片较多时,可以借助其命令行工具: /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $file_name对应的shell脚本: # 处理文件名出现空格问题 MY_IFS=$IFS IFS=$'\n' for file_name in $(find "$1" -name "*.png" -type f); do echo $file_name /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $file_name done IFS=$MY_IFS该工具还有一个Sketch插件:Plugin for Sketch ,可以在输出icon的时候直接进行压缩优化。 后台音频耗电问题 **功能:**一段助眠音频可以后台播放,播放完成后如果返回app,会跳到对应的完成界面。 **问题:**有用户反馈,听音频睡着了,早上醒来,电量掉了很多(40%)。 调研: 使用Xcode中 Debug Navigator栏的Energy Debug Gauge(需真机)测试。播放完成4分钟左右的后台耗电状态如下:分析可知虽然此时app停止了所有行为,但是生命周期仍处于Background状态,耗电量也还是存在的。暂时认为问题是客户端一直处于Background模式,并未被Suspend,导致一直被系统维护着,所以一直有耗电情况。 再一次查看苹果关于后台模式的文档:When the UIBackgroundModes key contains the audio value, the system’s media frameworks automatically prevent the corresponding app from being suspended when it moves to the background. As long as it is playing audio or video content or recording audio content, the app continues to run in the background. However, if recording or playback stops, the system suspends the app.可知,只要播放器未被销毁,就会一直占用后台模式! 解决方案: 在播放完成时销毁播放器。 检验: 再次观察播放音频完成一段时间之后的后台耗电情况:此时app被挂起,耗电量基本为0,唤醒app,仍能正常工作。问题解决。 推荐阅读 尝试分模块整理推荐阅读的内容 iOS开发过程中的设计模式设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。以上摘自菜鸟教程 Swift中的策略模式 从一个小例子入手,讲解策略模式在swift中的使用。 Swift 中的设计模式 #1 工厂方法与单例方法 Swift 中的设计模式 #2 观察者模式与备忘录模式 Swift 中的设计模式 #3 外观模式与适配器模式 SwiftGG翻译组翻译的设计模式系列文章。 设计模式资料整理 应该是目前总结最全的适合iOS开发的设计模式资料汇总。 iOS逆向iOS应用逆向工程,是指从目标应用的界面及功能表现入手,使用不同的工具和理论知识去分析其实现原理,得出应用的代码结构、整体设计、功能实现、执行流程等,然后利用iOS的系统知识和语言特性,借鉴或修改原有实现流程的技术。推荐两篇掘金上关于iOS逆向的文章: TikTok(抖音国际版)逆向,全球的小姐姐们,我来啦! 逆向 Mac 应用 Bartender 这两篇都是通过具体实例入手,讲解逆向常用的工具和一些逆向的思路,非常适合新手。最好跟着介绍实际操作一番,第一次成功逆向一个项目之后,那种爽歪歪的感觉,你懂的! 另外推荐一位逆向领域的牛人刘培庆(博客,github),了解逆向的同学大多应该都不陌生,iOS方面的逆向优先参照 MonkeyDev。 刘总今年出了新书《iOS应用逆向与安全》,非常适合对逆向有兴趣的同学。 利用脚本提高工作效率脚本语言(英语:Scripting language)是为了缩短传统的“编写、编译、链接、运行”(edit-compile-link-run)过程而创建的计算机编程语言。早期的脚本语言经常被称为批处理语言或工作控制语言。一个脚本通常是解释运行而非编译。脚本语言通常都有简单、易学、易用的特性,目的就是希望能让程序员快速完成程序的编写工作AppleScript入门:探索macOS自动化 文章选自少数派,讲述了:什么是 AppleScript? 我使用 AppleScript 的情境 AppleScript 基础语法 AppleScript suite 之外的解法:模拟键鼠非常清晰详细的AppleScript入门文章。 如何提高工作效率 - 自动化篇 文章选自掘金,串联Shell Script, Apple Script, Automator, Alfred & WorkFlows,告诉你如果灵活应用,达到利用脚本提高工作效率的目的。 我写了一个利用AppleScript和Alfred的WorkFlows在命令行打开指定路径的工具。on run argv tell application "Finder" -- get selection path set pathFile to selection as text set pathFile to get POSIX path of pathFile -- fix space problem in the directory set pathFile to quoted form of pathFile tell application "Terminal" activate tell window 1 do script "cd " & pathFile end tell end tell end tell end run使用方法是:点击选中文件夹,按下热键CMD + T。 音视频 创业那点事:听莲叔和羊叔回忆峥嵘岁月 莲叔和羊叔是 SwiftGG 翻译组的真·大佬。成绩好,学历高,思维缜密,能力很强。听过《升职加薪》那期节目的朋友应该都有印象。作为标准的技术人才,在创业大潮中自然也想试试身手,打拼一番。 和 Cee 聊聊如何拿 Google Offer 翻译组里真是人才辈出啊,前几天 Cee 在群里说了个好消息,他拿到了美国 Google 的 Offer。群友们纷纷表示祝贺并进行了隆重的认哥仪式,将 SwiftGG 诞生以来第一个 GG(哥哥) 称号颁发给我们公认的大哥 Cee。 以上两个音频来自于(ggtalk)[https://talk.swift.gg/],也是我最近上下班路上用来消遣的音频节目,墙裂推荐! 计算机科学速成课 由Carrie Anne Philbin主讲,Carrie Anne Philbin是一名计算机科学教师和作家。她是树莓派基金会的教育主管,也是学校(CAS)多样性和包容组的主席。她为青少年写了电脑书《树莓派历险记》。特意介绍是因为我感觉这个课程因为这个主讲人的个人魅力而趣味十足😄。不管你是不是计算机专业的学生,看过之后应该都能有所收获。 Github 12306ForMac 以前要么开Windows虚拟机,要么使用官方Web,现在可以使用12306ForMac订票助手啦。希望对大家有所帮助! LongestCocoa SoWhatIsTheLongestMethodOrConstantNamesInCocoaFramework? (那么,Cocoa框架中最长的方法名或常量名是什么?)答案是:outputImageProviderFromBufferWithPixelFormat:pixelsWide:pixelsHigh:baseAddress:bytesPerRow:releaseCallback:releaseContext:colorSpace:shouldColorMatch:kCMSampleBufferConduitNotificationParameter_UpcomingOutputPTSRangeMayOverlapQueuedOutputPTSRange这个项目有一定的调侃意味,so what? 我选择swift🤣

iOS开发月报#5|201811

大事件 11月27号,苹果AppStore下架包括拼多多、搜狗地图、讯飞阅读和悦跑圈在内的共718个App。据称下架原因是触发了AppStroe审核指南的2.5.2条例,即使用了热更新技术。去年6月份,苹果层因热更新下架数万款App。Tips 图片渲染开销 我们知道,解压后的图片是由无数像素数据组成。每个像素点通常包括红、绿、蓝和 alpha 数据,每个值都是 8 位(0–255),因此一个像素通常会占用 4 个字节(32 bit per pixel。少数专业的 app 可能会用更大的空间来表示色深,消耗的内存会相应线性增加)。 下面我们来计算一些通常的图片开销: 普通图片大小,如 500 * 600 * 32bpp = 1MB 跟 iPhone X 屏幕一样大的:1125 * 2436 * 32bpp = 10MB 即刻中允许最大的图片,总像素不超过1500w:15000000 * 32bpp = 57MB 有了大致的概念,以后看到一张图能简单预估,大概会吃掉多少内存。 关于iOS开发证书在钥匙串没有秘钥的问题 当前现象只需要将证书拖到登录,即可生成秘钥APP内切换语言 APP内切换语言有一种比较简单的实现方式,实现步骤是: 1、存储语言状态 2、刷新KeyWindow.rootViewController 3、刷新Bundle 4、切换语言对应的更新操作 实现过程: //1、切换语言,AppleLanguages对应内容是数组,语言符合可以通过Bundle.main.localizations查看 //切换至繁体中文,存储状态 UserDefaults.standard.set(["zh-Hant"], forKey: "AppleLanguages") //2、刷新keyWindow,createRootViewController为创建rootViewController的方法 UIApplication.shared.keyWindow?.rootViewController = Application.shared.createRootViewController()//3、更换bundle //在切换语言的时候,我们需要执行一下,Bundle的exchangeBundle方法 extension Bundle { static func getLocalizedBundle() -> Bundle { if let languages = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String], let lan = languages.first, let path = Bundle.main.path(forResource: lan, ofType: "lproj"), let bundle = Bundle.init(path: path){ return bundle } else { return Bundle.main } } static func exchangeBundle(){ //替换Bundle.main为自定义的BundleLocal object_setClass(Bundle.main, BundleLocal.self) } }class BundleLocal: Bundle { override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String { return Bundle.getLocalizedBundle().localizedString(forKey: key, value: value, table: tableName) } }//4、其他更新操作,如接口,UI界面测试内购时长 当我们测试内购时,需要考虑内购到期的情况。比如我测试购买了一个月的服务,那么购买到期的情况呢,不能傻傻地等一个月时间吧? 其实苹果给出了不同内购时长在Debug情况下对应的测试时间。AVPlayer进入前台自动播放 在我们不进行任何前后台设置的情况下,AVPlayer进入后台,会自动暂停播放。再次回到前台时,播放器会有一定概率自动恢复播放(多大概率未知,测试得出的结论)。 如果这个恢复播放不是我们需要的,或者我们需要避免这种不确定的情况发生,我们可以手动管理这个过程: NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: self, queue: nil) { (notification) in self.player.pause() }NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: self, queue: nil) { (notification) in //根据需要是否需要恢复播放 if shouleRecoverPlayer { self.player.play() } else { self.player.pause() }}iOS12.1关于tabbar的一个bug 触发条件:使用 UITabBarController + UINavigationController 组合 UITabBar带半透明效果,isTranslucent 属性为 true UIViewController的 hidesBottomBarWhenPushed 属性为 true 通过导航栏返回上一页时(导航栏返回按钮 or 屏幕左侧的滑动返回手势)问题现象:经确认是iOS12.1上的一个bug。 解决方案: //MARK: - fix ios12.1 tabbar bug let tabbar = UITabBar.appearance() tabbar.isTranslucent = false详细内容可以参考: 修复 iOS12.1 UITabBar 布局位移bug - ZHFDBK的博客 - CSDN博客 swift - UITabBar items jumping on back navigation on iOS 12.1 - Stack Overflow 推荐内容 重构Swift中的Identifiers 面对越来越多的identifier,改如何优雅地处理? 文中讲了一些UITableViewCell、UICollectionViewCell、segues、UIStoryboards、UIViewController中关于identifiers的重构方法。 The End Is Near for Mobile Apps 移动应用的末日将要来临? Medium科技板块最近比较火的一篇文章,分上下两篇,讲述移动应用的前景和趋势。 Realm学院 这是Realm建立的一个用于学习编程技术的网站,主要方式是视频,内容配有字幕,并且都全部整理成文章。涵盖范围有Realm, Android, iOS, JavaScript, Architecture等。 文章质量非常高! 开发利器 KTVHTTPCache 应该是iOS端目前最好的流媒体缓存框架了。 Realm Realm是一个开源的对象数据库管理系统,最初用于移动(Android/iOS),也可用于Xamarin或React Native等平台,等平台,包括桌面应用(Windows),并获得Apache许可。 对比FMDB,他的使用成本,维护成本都更低。 uTools uTools是一个极简、插件化、跨平台的现代化桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。 通过快捷键(默认alt+space)就可以快速呼出这个搜索框。它相当聪明,可以支持输入、拖拽、自动粘贴等作为输入源,相应的插件也早已准备就绪,统一的设计风格和操作方式,助你高效的得到正确的结果。 当你熟悉它后,能够为你节约大量时间,让你可以更加专注的改变世界。 对比Alfred,他的不同就在于免费,使用简单,更符合国人的习惯。

iOS开发月报#4|201810

记录本月开发遇到的知识点,小tips,和bug总结。 大事件 新版iPad Pro、MacBook Air、Mac mini发布,全线涨价,但是真香。。。Tips 适配swift4.2 1、利用xcode快速迁移 升级到Xcode10之后,我们打开项目会出现如下提示,点击会有一个版本升级窗口,如果你的的项目包含一些第三方库的话,第三方库的选型也会出现在上面:默认勾选第三方库,但是我们适配的时候不应该让Xcode去自动检索第三方库代码。只对我们的app进行代码迁移就够了。 适配完后可以在这里查看我们当前的swift版本:对于第三方库,如果都适配了swift4.2,那么更新到对应版本就行了。如果有没适配的,可以通过制定版本的方式解决冲突,在Podfile文件末尾添加如下代码: post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '4.0' end end end属性访问权限 swift中提供的访问权限关键词由低到高有以下五种: private < fileprivate < internal < public < open 其中internal是Swift中的默认控制级,一下介绍了这几种关键字的区别:private:当前作用域或者当前类中访问。 fileprivate:表示代码可以在当前文件中访问。 internal:在当前target中访问 public:可跨target使用,但不能被集成或者重写。 open:可跨target使用,允许被集成或者重写。对于一个严格的项目来说,精确的最小化访问控制级别对于代码的维护来说是很重要的。能用public的就别用open。 修改文件权限 当我们执行某些命令行操作,收到如下提示时: Linking /usr/local/Cellar/the_silver_searcher/2.1.0... Error: Could not symlink etc/bash_completion.d/ag.bashcomp.sh /usr/local/etc/bash_completion.d is not writable.就表明我们对需要修改的文件权限不够,我们可以给文件加上修改的权限: sudo chown -R $(whoami) /usr/local/etc/bash_completion.d其中-R表示对目前目录下的所有文件与子目录进行相同的拥有者变更(即以递回的方式逐个变更) 默认关键字 public static let `default` = ImageCache(name: "default")default是默认关键字,如果使用要加单斜号。 tabbar手动跳转 func tabBarController(UITabBarController, didSelect: UIViewController)In iOS v3.0 and later, the tab bar controller calls this method regardless of whether the selected view controller changed. In addition, it is called only in response to user taps in the tab bar and is not called when your code changes the tab bar contents programmatically. In versions of iOS prior to version 3.0, this method is called only when the selected view controller actually changes. In other words, it is not called when the same view controller is selected. In addition, the method was called for both programmatic and user-initiated changes to the selected view controller.tabbar的didSelect代理方法,只有在手动点击的时候才会触发。通过代码跳转是不会触发的。 自定义tabbar动画 // 当点击tabBar的时候,自动执行该代理方法(不需要手动设置代理) override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { for view in tabBar.subviews { //控制view实现各种动画效果 } }NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END 从xcode6.3开始,为了能让OC也能表示swift的?(optional)和!功能,增加了对对象的可选指定。指定属性是否可选,可以: @property (nonatomic, copy, nonnull) NSString * tickets; //或者 @property (nonatomic, copy) NSString * __nonnull tickets;如果属性多了,每一个都这么写会很麻烦,苹果增加了一对新的宏命令,就是NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。放在里面的对象就相当于都增加了nonnull命令,nonull为默认值。 一个自定义collectionView布局的bug bug描述: NSInternalInconsistencyExceptionUICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x280d2b200> {length = 2, path = 1 - 5}原因: layoutAttributesForElementsInRect返回的UICollectionViewLayoutAttributes数组有indexPath没有被 [NSIndexPath indexPathForRow:numberOfSection]覆盖。 当数据量增加时不会出问题,当数量减少时出现。有人反映这是iOS10的bug,但实际上,我拿iOS10模拟器跑并没有问题,反而是在崩溃后台看到是iOS12的用户上报的。那究竟什么原因不详,附stackoverflow(iOS 10 bug: UICollectionView received layout attributes for a cell with an index path that does not exist - Stack Overflow]地址。 解决方案: 当我们自定义layout时,需要清除UICollectionViewLayoutAttributes的缓存 //方案一: 在自定义layout的类里 override func prepare() { super.prepare() attributesArr.removeAll() } //方案二:在collectionview刷新出 collectionView.reloaData() collectionView.collectionViewLayout.invalidateLayout()配置git SSH 1、设置git的user name和email $ git config --global user.name "username" $ git config --global user.email "username@gmail.com"2、生成秘钥 $ ssh-keygen -t rsa -C "username@gmail.com"如果不需要设置密码,连按三个回车。最后得到了两个文件:id_rsa和id_rsa.pub。 3、添加秘钥到ssh-agent中 $ ssh-add ~/.ssh/id_rsa4、复制公钥内容到剪贴板 $ pbcopy < ~/.ssh/id_rsa.pub5、登录git仓库(Github或者Bitbucket)将公钥内容粘贴到指定配置位置。 Github地址为:https://github.com/settings/keys Apple Watch开发注意事项 1、watch没有UIKit,对于UI的操作只能通过storyboard进行 2、watch只支持帧动画,我们只能通过png序列来实现动画效果。WKInterfaceGroup 和 WKInterfaceImage均可以实现帧动画。 3、开发的watch应用内存被限定为80M,太多帧的动画会不支持 4、提交应用watch也需要配置市场截图。 5、watch分为两个target。当新建一个Target为WatchDemo,xcode会自动生成一个WatchDemo Extension。前者负责UI,后者负责逻辑。引用cocoapods可以这么写: target 'WatchDemo Extension' do platform :watchos, '3.0' use_frameworks! pod 'Alamofire' end6、Always Embed Swift Standard Libraries 在Build Settings里面,这两个target,需要将WatchDemo Extension中设置为Yes,另一个设置为No Github Sizes 可以在一个界面,显示各个屏幕尺寸。这样我们就不用每个模拟器跑一遍看效果了。方便调试。 iOS-DeviceSupport 当手机升级,而xcode未升级时,我们会遇到Device Support的弹框,此时要么升级xcode,要么需要导入对应的Device Support文件。这个库就是提供这种文件的。 InjectionIII 用于解决烦人的UI调试问题。当修改了一些UI属性之后,在xcode中我们只能运行程序才能看到效果,如果是处理大量的UI问题,这个过程是很烦人的。好在InjectionIII帮我们解决了这个问题,一起了解一下吧!

iOS开发月报#3|201809

新机发布,你中意XS Max还是XR?iOS Tips 在label中插入图片 let label = UILabel() let attribute = NSMutableAttributedString(string: "Title")let imgAttch = NSTextAttachment() imgAttch.image = image //设置图片大小 imgAttch.bounds = CGRect.init(x: 0, y: 0, width: 18, height: 15) let imageAttribute = NSAttributedString(attachment: imgAttch) //图片插入位置 attribute.insert(imageAttribute, at: 0) label.attributedText = attributeCollectionCell阴影+圆角的优雅处理方式 只针对类似App Store的整体圆角阴影的效果。 //设置父视图阴影效果 //CollectionView.swift self.layer.shadowOffset = offset self.layer.shadowColor = color.cgColor self.layer.shadowRadius = radius self.layer.shadowOpacity = opacity contentView.layer.cornerRadius = radius //如果切割圆角带imageView才需要添加下面 contentView.layer.masksToBounds = true处理tableView点击时label背景色消失问题 //第一种方式,UITableViewCell.swift override func setHighlighted(_ highlighted: Bool, animated: Bool) { let color = self.indexLabel.backgroundColor super.setHighlighted(highlighted, animated: animated) self.indexLabel.backgroundColor = color } //第二种方式 cell.textLabel.backgroundColor = UIColor.clear cell.textLabel.layer.backgroundColor = UIColor.red调整UILabel文字的内边距 自定义UILabel,然后重写drawText:方法 override func drawText(in rect: CGRect) { let insets = UIEdgeInsets.init(top: 20, left: 20, bottom: 20, right: 20) super.drawText(in: UIEdgeInsetsInsetRect(rect, insets)) }swift 浮点数取整 ceil(x)返回不小于x的最小整数值。 floor(x)返回不大于x的最大整数值。 round(x)返回x的四舍五入整数值。 let number1 = 12.456 // ceil(number1) = 13.0, floor(number1) = 12.0, round(number1) = 12.0 let number2 = 12.756 // ceil(number2) = 13.0, floor(number2) = 12.0, round(number2) = 13.0跳过非store下载的应用检查 xattr -d com.apple.quarantine app所在路径 加载大图导致内存暴涨 large_leaves_70mp.jpg图片是7033x10110(占用磁盘大小8.3MB),分辨率 = 7033 x 10110 x 4(ARGB),对应位图占用大小 = 分辨率 x 1024 x 1024 ( = 271MB),解压会把图片转成位图,也就意味着会占用271MB内存,所以解压过程内存会瞬间消耗很大,等转成NSData后位图的内存就会被回收掉,内存就降下来,这时候NSData占用的大小即是图片的实际大小,该过程中由于会转成位图,而位图的大小是比图片的实际的大小大很多的,内存暴增的点就在位图。位图的内存大小计算是根据图片的分辨率而来(分辨率(width x heigth) x 1024 x 1024 x 4 (ARGB)),所以一般来说图片分辨率越高转成的位图占用的内存空间越大。 新版iphone尺寸设备 逻辑分辨率 比例因子 对角线 分辨率iPhone XS Max 414×896 @3x 6.5inch 1242px × 2688pxiPhone XS 375×812 @3x 5.8inch 1125px × 2436pxiPhone XR 414×896 @2x 6.1inch 828px × 1792pxiPhone X 375×812 @3x 5.8inch 1125px × 2436pxiPhone 8 Plus 414×736 @3x 5.5inch 1242px × 2208pxiPhone 8 375×667 @3x 4.7inch 750px × 1334pxiPhone SE 320×568 @2x 4inch 640px × 1136pxiPhone 4 320×480 @2x 3.5inch 640px × 960px带透明度的渐变 使用CAGradientLayer进行渐变的时候,如果我们需要由一个颜色渐变至透明,当我们将透明色写成UIColor.clear或者其他通过RGBA设置的颜色,改变透明度为0时,会发现实际效果跟预期有出入,会带点黑色:这是因为clearColor会有一个透明度为0的黑色通道。所以应该这样设置透明色: UIColor(white: 1, alpha: 0).cgColor指定tableView,collectionView的header高度0 如果我们想隐藏headerView可能会直接在其高度的代理方法,做如下设置: func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 0 }当实际上这并不会生效,会返回一个默认高度。也就是说这个高度只有设置成一个非0的正数才是有效的。 有些人会写成0.01,表面上看是解决问题了,但0.01的偏移会造成像素不对齐(Color Misaligned Images),加重CPU计算负荷。完美的解决方案是: func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return CGFloat.leastNonzeroMagnitude }CGFloat.leastNonzeroMagnitude表示CGFloat支持的最小正数值,不会引起像素偏移。 保持屏幕常亮 //保持屏幕常亮 UIApplication.shared.isIdleTimerDisabled = true //关闭屏幕常亮 UIApplication.shared.isIdleTimerDisabled = false注意:不要滥用屏幕常亮属性(苹果会因为这打回你的app),如果只在某些特殊场合需要屏幕常亮,应该在之后将该值恢复成默认值false。 Github MMKV MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今,在 iOS 微信上使用已有近 3 年,其性能和稳定性经过了时间的验证。近期也已移植到 Android 平台,一并开源。 替代UserDefaults的绝佳方案。 Lottie Lottie是一个面向Android和iOS的移动库,它能够解析由AE在bodymovin导出为json的效果动画,并在移动端渲染矢量动画! 一些复杂的UI可以完全交给设计师了,哈哈哈。 RSSHud RSSHub 是一个轻量、易于扩展的 RSS 生成器,可以给任何奇奇怪怪的内容生成 RSS 订阅源。 结合Reeder不要太爽哦。😆

iOS开发月报#2|201808

数据库用完要close 当我们向下面这样执行完一次数据可查询时,要记得将数据库关闭,否则,如果此时想往同一数据库写东西的话会因为数据正在锁定收到这样的提示database is locked。 //获取下载完成的文件信息 func isExistdWith(_ id: String) -> Bool{ guard db.open() else { return false } do { let resultSet = try db.executeQuery("select * from tableName where id = ?", values: [id]) if resultSet.next() {let isCompleted = resultSet.bool(forColumn: self.isCompleted) db.close()//return之前要close数据库 return isCompleted } } catch {} db.close()//return之前要close数据库 return false }UIDatePicker的时间格式 当我们用UIDatePicker做选择时间的控件时,DatePicker会根据手机时间的设置自动选择是12小时制还是24小时制,如果我们需要强制控制DatePicker是显示12小时制还是24小时制可以这么做: datePicker.datePickerMode = .time datePicker.locale = Locale.init(identifier: "en_GB")//for 24 Hrs datePicker.locale = Locale.init(identifier: "en_US")//for 12 HrsiOS skill map变量对外只读,对内可读写 struct Person { private(set) var name : String! }设置UITableViewCell分割线对齐 默认的cell分割线都是偏向右边多一些的,如果我们想让分割线对齐的话,正确的做法是: tableView.separatorInset = UIEdgeInsets.init(top: 0, left: 40, bottom: 0, right: 40)设置左右边距都是40 但是使用这种方法会带来一个问题就是默认的textLabel会跟着右移。为了保持label的居中我们可以再加一句: tableView.separatorInset = UIEdgeInsets.init(top: 0, left: 40, bottom: 0, right: 40) tableView.layoutMargins = UIEdgeInsets.init(top: 0, left: 40, bottom: 0, right: 40)富文本显示图片元素 如果我们需要文字中插入图片元素时,可以使用富文本处理: let attch = NSTextAttachment() attch.image = UIImage.(named:"logo") attch.bounds = CGRect.init(x: 0, y: 0, width: 18, height: 18) let imageAttribute = NSAttributedString(attachment: attch) titleLabel.attributedText = attributed添加spotlight搜索索引 首先导入CoreSpotlight和MobileCoreServices框架,然后加入以下代码: var searchableItems = [CSSearchableItem]() //索引项 let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeData as String) //title attributeSet.title = "Item Title" //desription attributeSet.contentDescription = "match.description" //thumb attributeSet.thumbnailData = try? Data.init(contentsOf: URL(string: url)!) //keywords attributeSet.keywords = ["Love", "Peace"] let searchableItem = CSSearchableItem(uniqueIdentifier: "app_keywords", domainIdentifier: "com.company.app", attributeSet: attributeSet) searchableItems.append(searchableItem) //建立索引 CSSearchableIndex.default().indexSearchableItems(searchableItems) { (error) -> Void in if error != nil { print(error?.localizedDescription ?? "Error") } }Hexo编译问题 在执行hexo g编译markdown文件时莫名报错: TypeError: Cannot set property 'lastIndex' of undefined解决方案是在_config.yml中将auto_detect设为false Podfile用法 # 下面两行是指明依赖库的来源地址 source 'https://github.com/CocoaPods/Specs.git' source 'https://github.com/Artsy/Specs.git'# 说明平台是ios,版本是8.0 platform :ios, '8.0'# 忽略引入库的所有警告(强迫症者的福音啊) inhibit_all_warnings!# 生成的是framework而不是静态库 use_frameworks!# 针对MyApp target引入AFNetworking # 针对MyAppTests target引入OCMock, target 'MyApp' do pod 'AFNetworking', '~> 3.0' target 'MyAppTests' do inherit! :search_paths pod 'OCMock', '~> 2.0.1' end pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec' # 引入内部库 pod 'ABTest', :git => 'https://bitbucket.org/sealcn/remoteabtest.git' pod 'ABTest', :git => 'https://bitbucket.org/sealcn/remoteabtest.git', :tag=> '0.0.6' pod 'ABTest', :git => 'https://bitbucket.org/sealcn/remoteabtest.git', :commit=> '082f8319af' # 编译配置,指定仅在Debug模式下启用 pod 'Reveal-SDK', :configurations => ['Debug'] # 使用本地文件 pod 'AFNetworking', :path => '~/Documents/AFNetworking' # 指定版本号0.1.3到0.2,不包括0.2 pod 'CHIPageControl', '~> 0.1.3' # 仅安装QueryKit下的Attribute和QuerySet模块 pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']end # 这个是cocoapods的一些配置,官网并没有太详细的说明,一般采取默认就好了,也就是不写. post_install do |installer| installer.pods_project.targets.each do |target| puts target.name end end

iOS开发月报#1|201807

关闭隐式动画 CATransaction.begin() CATransaction.setDisableActions(true) self.layer.frame = self.bounds CATransaction.commit()AVPlayer出现一直缓存,缓存一段时间之后才开始播放的问题 player.automaticallyWaitsToMinimizeStalling = false//延迟播放,默认开关于这个属性的一些说明:In versions of iOS prior to iOS 10.0 and versions of OS X prior to 10.12, this property is unavailable, and the behavior of the AVPlayer corresponds to the type of content being played. For streaming content, including HTTP Live Streaming, the AVPlayer acts as if automaticallyWaitsToMinimizeStalling is YES. For file-based content, including file-based content accessed via progressive http download, the AVPlayer acts as if automaticallyWaitsToMinimizeStalling is NO.大致是说在iOS10之前的客户端,虽然这个参数不可用,但是非流媒体类型的播放这个配置默认为false,所以在iOS10下建议这个属性值为false。 AVPlayer是否正在播放的判断 当我们使用KVO监听player.rate来判断player的是否正在播放时,会发现这个值是不准的。其实准确的说是player.rate=1不代表正在播放,player.rate=0是可以代表正在暂停的。所以player.rate=0代表暂停,正在播放的状态可以这样判断: self.timeObserve = self.player.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 1), queue: DispatchQueue.main, using: {(time) in if self.player.timeControlStatus == AVPlayerTimeControlStatus.playing { //AVPlayerTimeControlStatus为iOS之后的API self.state = .playing } })下载时URLSessionConfiguration的配置 使用Alamofire下载时,我们通常需要一个SessionManager配置下载参数: let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 50//50s超时 /** 最大同时下载数 ---- iOS对于同一个IP服务器的并发最大默认为4,OS X为6 */ configuration.httpMaximumConnectionsPerHost = 4 /** A Boolean value that indicates whether TCP connections should be kept open when the app moves to the background. */ configuration.shouldUseExtendedBackgroundIdleMode = true//为true支持后台下载 manager = Alamofire.SessionManager(configuration: configuration)不要存储沙盒绝对地址 当我们向沙盒写入数据时,将该绝对路径保存下来,下次再打开该地址并不会获取到我们存入的数据。原因如下: iOS8之后,苹果添加的新特性,将每次打开app内的沙盒[唯一编码路径](红框部分)重新生成,并保持上一次的沙盒文件(Documents、Library、tmp)移到新生成的文件内,旧文件删除,就是说,你保存的文件都在,只不过每次打开后,都会有一个新的绝对路径。所以存储路径应该存相对路径: //这两个都代表document的相对路径 let rootPath = NSHomeDirectory() + "/Documents/" let rootPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).firstchildViewController的viewDidAppear方法调用 如果一个ViewController中嵌套了多个childViewController。当宿主VC(我们暂且这么称呼它)调用viewDidAppear等方法时,其中的childViewController都会默认调用对应方法。如果我们不想childViewController调用该方法可以重写该VC的属性: override var shouldAutomaticallyForwardAppearanceMethods: Bool { return false }图片切换渐入渐出的方法 通过UIImageView展示图片和layer.contents展示图片都可以使用以下方法: let transition = CATransition() transition.duration = 0.5 transition.type = kCATransitionFade self.view.layer.add(transition, forKey: "layer.contents") self.view.layer.contents = image.cgImage//适用于imageViewcell移出视图,移入视图的方法 //TableViewCell override func prepareForReuse() { super.prepareForReuse()//使用重用池的cell,显示过的cell移至可视范围 } //TableView func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { //cell移出视图时调用 }tableview,collectionView数据reload之后的操作 我们如果要想实现在reload之后弹出alertView,或者滚动到特定一行,可能会直接写: tableView.reloadData() tableView.scrollToRow(at: indexPath, at: .middle, animated: true)看似没问题,但是滚动没起作用,因为reloadData是立即返回的,不会等tableview刷新完成。 解决办法就是需要等reload完成之后再做我们需要的操作,reload是否完成有几种方式监听: //collectionView collectionView.performBatchUpdates(nil) { (finished) in //reload完成 } //tableView方法只有iOS11可用 tableView.performBatchUpdates(nil) { (finished) in //reload完成 }//替代func beginUpdates(),func endUpdates() //tableView等reload完成还可以使用 tableView.reloadData() DispatchQueue.main.async { //reload完成 }