【译】设计模式引导--OOP的能力
- 20 Aug, 2019
原文链接 作者:Hitendra Solanki导读--本博客系列要求具有面向对象编程的中级专业知识。您应该对类、对象、构造函数、继承、值和引用类型有基本的了解。通过仔细地从头到尾阅读本系列文章,不管是中级还是高级开发,您都将有所收获。设计模式用于表示经验丰富的面向对象软件开发人员社区采用的最佳实践。 建造者模式帮助我们更简单更易读地创建一个类,它遵守着以下两条规则: 1、分割原始类和它的构造方法 2、在最后一个返回类的实例 建造者模式最佳的例子就是SwiftUI,是的你没有看错。SwiftUI中大部分类像是Text,Image都是使用的建造者模式。 问题: 想一下,一个Person类拥有不少于十个属性,当你要使用它时,你需要为它创建一个构造方法。它的构造者将拥有不少于十个参数,去管理这么一个带有很多参数的单一函数或构造方式将是非常困难的,最终你也会让这端代码失去可读性。看下面的例子: class Person { //personal details var name: String = "" var gender: String = "" var birthDate: String = "" var birthPlace: String = "" var height: String = "" var weight: String = "" //contact details var phone: String = "" var email: String = "" //address details var streeAddress: String = "" var zipCode: String = "" var city: String = "" //work details var companyName: String = "" var designation: String = "" var annualIncome: String = "" //constructor init(name: String, gender: String, birthDate: String, birthPlace: String, height: String, weight: String, phone: String, email: String, streeAddress: String, zipCode: String, city: String, companyName: String, designation: String, annualIncome: String) { self.name = name self.gender = gender self.birthDate = birthDate self.birthPlace = birthPlace self.height = height self.weight = weight self.phone = phone self.email = email self.streeAddress = streeAddress self.zipCode = zipCode self.height = height self.city = city self.companyName = companyName self.designation = designation self.annualIncome = annualIncome } }//This is function in Xcode-Playground which executes our test code func main() { let hitendra = Person(name: "Hitendra Solanki", gender: "Male", birthDate: "2nd Oct 1991", birthPlace: "Gujarat, India", height: "5.9 ft", weight: "85kg", phone: "+91 90333-71772", email: "hitendra.developer@gmail.com", streeAddress: "52nd Godrej Street", zipCode: "380015", city: "Ahmedabad", companyName: "Fortune 500", designation: "Software architect", annualIncome: "45,000 USD") //use of Person object print("\(hitendra.name) works in \(hitendra.companyName) compay as a \(hitendra.designation).") }//call main to execute our test code in Xcode-Playground main()/* Console output: Hitendra Solanki works in Fortune 500 compay as a Software architect. */将上面的例子在playground中运行一下,你会得到预期结果。逻辑上这也是对的。 我们可以尝试优化上面的代码,从解决这两个问题入手。 1、我们必须按照既定的顺序传参数,而不能通过重新排列参数提高可读性。 2、即使创建对象时我们不知道一些属性值,我们也不得不传入所有参数。 例如你需要创建一个Person类,但是这个人还在找工作。只有当他进入某一公司我们才能得到他的工作信息。 解决方案: 1、创建相关属性的逻辑分组。 2、为不同分组的属性创建不同的建造者类。 3、在建造者类中最后一步返回实例。 让我们从上面的例子开始,我们已经拥有一个Person类,它含有14个属性。我们仔细观察这14个属性,可以将它分为四组。 1、个人信息 2、联系方式 3、地址信息 4、公司信息 通过强大的设计模式我们可以解决上面两个问题,具体代码如下: //This is function in playground which executes our test code func main() { var hitendra = Person() //person with empty details let personBuilder = PersonBuilder(person: hitendra) hitendra = personBuilder .personalInfo .nameIs("Hitendra Solanki") .genderIs("Male") .bornOn("2nd Oct 1991") .bornAt("Gujarat, India") .havingHeight("5.9 ft") .havingWeight("85 kg") .contacts .hasPhone("+91 90333-71772") .hasEmail("hitendra.developer@gmail.com") .lives .at("52nd Godrej Street") .inCity("Ahmedabad") .withZipCode("380015") .build() //use of Person object print("\(hitendra.name) has contact number \(hitendra.phone) and email \(hitendra.email)") //later on when we have company details ready for the person hitendra = personBuilder .works .asA("Software architect") .inCompany("Fortune 500") .hasAnnualEarning("45,000 USD") .build() //use of Person object with update info print("\(hitendra.name) works in \(hitendra.companyName) compay as a \(hitendra.designation).") }//call main to execute our test code main()//Person class which only contains the details class Person { //personal details var name: String = "" var gender: String = "" var birthDate: String = "" var birthPlace: String = "" var height: String = "" var weight: String = "" //contact details var phone: String = "" var email: String = "" //address details var streeAddress: String = "" var zipCode: String = "" var city: String = "" //work details var companyName: String = "" var designation: String = "" var annualIncome: String = "" //empty constructor init() { } }//PersonBuilder class helps to construct the person class instance class PersonBuilder { var person: Person init(person: Person){ self.person = person } //personal details builder switching var personalInfo: PersonPersonalDetailsBuilder { return PersonPersonalDetailsBuilder(person: self.person) } //contact details builder switching var contacts: PersonContactDetailsBuilder { return PersonContactDetailsBuilder(person: self.person) } //address details builder switching var lives: PersonAddressDetailsBuilder { return PersonAddressDetailsBuilder(person: self.person) } //work details builder switching var works: PersonCompanyDetailsBuilder { return PersonCompanyDetailsBuilder(person: self.person) } func build() -> Person { return self.person } }//PersonPersonalDetailsBuilder: update personal details class PersonPersonalDetailsBuilder: PersonBuilder { func nameIs(_ name: String) -> Self { self.person.name = name return self } func genderIs(_ gender: String) -> Self { self.person.gender = gender return self } func bornOn(_ birthDate: String) -> Self { self.person.birthDate = birthDate return self } func bornAt(_ birthPlace: String) -> Self { self.person.birthPlace = birthPlace return self } func havingHeight(_ height: String) -> Self { self.person.height = height return self } func havingWeight(_ weight: String) -> Self { self.person.weight = weight return self } }//PersonContactDetailsBuilder: update contact details class PersonContactDetailsBuilder: PersonBuilder { func hasPhone(_ phone: String) -> Self { self.person.phone = phone return self } func hasEmail(_ email: String) -> Self { self.person.email = email return self } }//PersonAddressDetailsBuilder: update address details class PersonAddressDetailsBuilder: PersonBuilder { func at(_ streeAddress: String) -> Self { self.person.streeAddress = streeAddress return self } func withZipCode(_ zipCode: String) -> Self { self.person.zipCode = zipCode return self } func inCity(_ city: String) -> Self { self.person.city = city return self } }//PersonCompanyDetailsBuilder: update company details class PersonCompanyDetailsBuilder: PersonBuilder { func inCompany(_ companyName: String) -> Self { self.person.companyName = companyName return self } func asA(_ designation: String) -> Self { self.person.designation = designation return self } func hasAnnualEarning(_ annualIncome: String) -> Self { self.person.annualIncome = annualIncome return self } }/* Console output: Hitendra Solanki has contact number +91 90333-71772 and email hitendra.developer@gmail.com Hitendra Solanki works in Fortune 500 compay as a Software architect. */在上面的例子中,我们把Person类根据职责分割成了几个不同的类。我们创建了多个建造者,他们分别管理相关分组内的属性,而Person只持有这些建造者。 我们拥有一个建造者基类PersonBuilder和四个衍生的建造者类,PersonPersonalDetailsBuilder, PersonContactDetailsBuilder, PersonAddressDetailsBuilder 和 PersonCompanyDetailsBuilder。 当其他四个从Personbuilder衍生出来的建造者需要更新相关属性时,Personbuilder这个基类可以帮助我们在它们之间进行转换。 在上面的例子中我们可以看到新的构造方法变得更加易读了,我们可以用一种更加优雅的方式更新一组或者某一个属性。 需要注意一下,上面的例子中我们再每个建造者更新方法之后返回了它自己。这让我们能够在相同的建造者中写出链式方法,而不是分开的多行。这个概念称为流程模式。 优点 1、用一种优雅的方式很容易地初始化一个含很多参数的类。 2、遵从单一职责原则。 3、根据你的情况,以任意的顺序初始化对象和更新属性。
iOS国际化及本地化(一)不同语言的差异处理及测试
国际化及本地化概念 将标题取名为国际化及本地化(internationalization and localization),是因为这两个概念是有差异的,而这个差异常常被我们忽略,以下是维基百科的解释:国际化是指在设计软件,将软件与特定语言及地区脱钩的过程。当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。本地化则是指当移植软件时,加上与特定区域设置有关的信息和翻译文件的过程。 国际化和本地化之间的区别虽然微妙,但却很重要。国际化意味着产品有适用于任何地方的“潜力”;本地化则是为了更适合于“特定”地方的使用,而另外增添的特色。用一项产品来说,国际化只需做一次,但本地化则要针对不同的区域各做一次。这两者之间是互补的,并且两者合起来才能让一个系统适用于各地。有些时候我们也会用国际化或者全球化代替这两者含义。 作为一款优秀的产品我们做多语言版本时不应仅仅考虑到翻译这一层面,还有更多本地化相关内容需要我们注意,这篇文章主要涉及的也是本地化这一块。 国际化工作流程本篇文章主要介绍Internationalize和Test这两步。 本文目录为:1.增加多语言 2.UI元素的本地化 3.资源文件本地化 4.字符串相关的本地化 5.使用NSLocal进行本地化 6.从右到左语言的处理 7.本地化测试增加多语言 1、在项目导航栏选择项目(不是target) 2、在Localizations一栏,点击“+”号,添加语言每个条目都是由语言名称和语言id构成,例如Chinese(Simplified)(zh-Hans), Japanese(ja) 至此我们的项目就开启了对应语言的本地化支持。 3、在对话框中选中你想本地化的文件。 语言和区域的影响通过观察系统日历,我们可以看到即使语言一样,国家区域的不一致也会有一些约定上的区别,关于日期和时间的本地化会在下面介绍。 1、语言设置:Setting -> General -> Language & Region同样的,关于Region和Calendar的设置也在该页面。 资源文件的本地化 storyboard, xib文件 对于sotryboard和xib文件的本地化是Xcode直接支持的。 在添加语言时会提示我们自动选择创建本地化文件,如果是在添加语言之后创建的IB文件,可以通过xcode右侧属性栏中点击Localize...生成本地化文件。 图片文件 1、方法一 对于图片内容我们可以通过同IB文件的方式进行本地化,但是有一个限制就是图片要是放到项目文件层级的,而不能放到Assets文件夹中。 好消息是Xcode 11将放开这种限制,对于Assets引入的图片也可以做本地化处理。 2、方法二 除了Xcode本身支持的方式,我们还可以通过命名来区分图片内容,把图片名当做需要本地化的字符串,各个语言对应不同版本的图片名,这样也可以实现图片文件的本地化。 音视频及其他资源文件 如果是内置的像是音视频,json或者其他类型的配置文件这类内容,可以使用图片文件的方法二进行引入。 更多详细的设置可以参考这个文章:iOS语言国际化/本地化-实践总结 UI元素的本地化 使用Auto layout Auto layout是相对布局,它有能力在语言和区域变化时进行自适应。以下有几点使用auto layout的技巧: 1、移除宽度的约束 相同含义下不同语言宽度往往不一样,应该让控件能够自适应。 2、使用内容内部大小 fields和label默认是自动调整大小的,如果一个显示本地化内容的视图需要这个功能,选择该view,选择Editor > Size To Fit Content 3、使用leading和trailing属性 正常leading和trailing对应left和right,他们含义相同。但是有些国家,像是希伯来和阿拉伯的人使用习惯是从右往左。如果你是使用leading和trailing,在该环境下将自动对应right和left。 4、将视图固定到相邻视图 就是定义相邻约束,避免某一视图变化导致重叠。 使用伪本地化发现问题 这个功能只支持使用storyboard和xib进行布局的UI。 1、选中需要测试的.storyboard或者.xib文件 2、选择菜单栏 View > Assistant Editor > Show Assistant Editor字符串相关的本地化 使用Unicode字符串 对于所有面向用户的字符串都要使用NSString, NSAttributedString,对于Swift就是String, AttributedString,因为他们支持Unicode,Unicode是世界上所有书写系统的字符编码标准。 对于一些特殊的字符串需求: 1、访问字符串中的字符 使用NSString中的rangeOfComposedCharacterSequenceAtIndex:和 rangeOfComposedCharacterSequencesForRange:方法,他们会确保你在取字符串时不会破坏原文本。看一个例子你可能会明白:这两个文字无论是在UTF-16还是UTF-32编码的情况下都是不同的长度,所以我们不能通过长度而要通过以上的两种方式取目标字符串。 2、遍历字符串 如果我们要遍历展示下面的字符串:可以通过enumerateSubstringsInRange:options:usingBlock:方法,其中options参数如果传递NSStringEnumerationByComposedCharacterSequences将会按照最小字符进行遍历,如果选用NSStringEnumerationByWords将会按照词语进行遍历。 以上例子使用该值遍历的结果是:更多关于字符串相关的本地化问题可以参照该条视频: WWDC 2013 Making Your App World-Ready3、关于人名,邮寄地址,电话号码的检测 因为不同国家对于人名和电话号码的规则差别较大,我们可以针对不同国家写正则进行检测,也可以使用苹果提供的一个特殊含义字符的检测类:NSDataDetector 支持检测的类型包括日期,地址,链接,手机号,交通信息。 获取当前语言 将语言设置为English(United Kingdom),区域设置成United States,通过以下API获取到: //en NSString *languageID = [[NSBundle mainBundle] preferredLocalizations].firstObject;一般获取语言所用的方式是通过Bundle也就是第一种方式。 使用NSLocal进行本地化 NSLocale对象封装关于特定区域格式化标准的信息,包括日期,时间,测量,数字,货币等一系列内容。 将语言设置为English(United Kingdom),区域设置成United States,通过以下API获取到: //en-GB_US [NSLocale currentLocale].localeIdentifier; //en [NSLocale currentLocale].languageCode;其中languageCode跟通过Bundle获取到的是一样的。 其中localIdentifier表示为en-GB_US,对应为:语言id-国家id_区域码,这几个内容都可以通过NSLocal对象取到。 获取特定语言的引号 因为每种语言对于引号的使用是不一样的,我们可以通过NSLocal获取到引号 //1.Get the language that the app is using. NSString *languageID = [[NSBundle mainBundle] preferredLocalizations].firstObject; //2.Get the associated locale object. NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageID]; //3.Get the beginning and ending symbols for quotes from the locale object. bQuote = [locale objectForKey:NSLocaleQuotationBeginDelimiterKey]; eQuote = [locale objectForKey:NSLocaleQuotationEndDelimiterKey]; //4.Format a string using the locale-sensitive quotes. quotedString = [NSString stringWithFormat:@"%@%@%@", bQuote, myText, eQuote];以下展示了不同区域对于myText为@"iPhone"时的字符串效果。字符串的本地化 1、创建格式化字符串 应该使用localizedStringWithFormat:而不是stringWithFormat:。 NSString *localizedString = [NSString localizedStringWithFormat:@"%3.2f", myNumber];此方法会根据系统Local进行显示。 日期时间本地化 2、日期和时间转字符串 使用NSDateFormatter表示NSDate对象。推荐使用这个方法:localizedStringFromDate:dateStyle:timeStyle:。 //14 Aug 2019 at 11:19 NSString *localizedDateTime = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterShortStyle];下表展示了语言为英语,区域是美国时的日期和时间格式:下表展示了dateStyle为NSDateFormatterMediumStyle,timeStyle为NSDateFormatterShortStyle在不同语言和地区时的表现形式:3、使用自定义日期和时间格式 //1.Create an NSDateFormatter object. NSDateFormatter *dateFormatter = [NSDateFormatter new]; //2.get a localized format string from a template that you provide. NSString *localeFormatString = [NSDateFormatter dateFormatFromTemplate:@"MMM d" options:0 locale:dateFormatter.locale]; //3.Set the format of the NSDateFormatter instance to the locale-sensitive format string. dateFormatter.dateFormat = localeFormatString; //4.Use the stringFromDate: method to get a localized string representation of the date. NSString *localizedString = [dateFormatter stringFromDate:[NSDate date]];在不同语言和区域下localizedString对应的内容为:3、解析日期字符串 //1.Create a date formatter object. NSDateFormatter *dateFormatter = [NSDateFormatter new]; //2.Set the formatter’s style to a preset style. dateFormatter.dateStyle = NSDateFormatterMediumStyle; //3.If the input string is not expected to contain a time, set the time style to none. dateFormatter.timeStyle = NSDateFormatterNoStyle; //4.Set the leniency to YES (enables the heuristics). dateFormatter.lenient = YES; //5.Convert the string to a date object. NSDate *date = [dateFormatter dateFromString:inputString];我们输入的字符串是9/3/14,dateStyle设为NSDateFormatterShortStyle,如果区域为美国,我们得到的NSDate信息为:2014-09-03 07:00:00 +0000,如果区域为德国,我们将得到2014-03-09 08:00:00 +0000。 数字本地化 本地化设置会影响小数点符号,千分符,货币符等内容,比如数字1,234.56在意大利应该表示为1.234,56,所以对于数字的格式化我们应该用NSNumberFormatter处理。 注意:NSNumberFormatter不是现成安全的 1、将Number转成本地化的字符串 可以使用NSNumberFormatter的localizedStringFromNumber:numberStyle:方式 NSString *localizedString = [NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterDecimalStyle];以下是不同语言和区域关于数字的显示效果,左侧的style及numberStyle:2、将字符串转成NSNumber对象 这个类似日期的转换 //1.Create a number formatter object. NSNumberFormatter *numberFormatter = [NSNumberFormatter new]; //2.Set the formatter’s style to a preset style. numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; //3.Set the leniency to YES (enables the heuristics). numberFormatter.lenient = YES; //4.Convert the string to a number object. NSNumber *number = [numberFormatter numberFromString:inputString];3、通过NSCalendar计算日期 NSCalendar类封装了日历的所有区域差异和复杂性。说他具有复杂性是因为在不同国家,一年之中的月份可能是12或者13,一月中的天数可能是5到31的任意值,每周第一天可能是周六,周日或者周一。可以看下表因此使用NSCalendar取这些值将会很方便。 获取Calendar unit的方式为 //1.Create an NSDateComponents object. NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSEraCalendarUnit fromDate:[NSDate date]]; //2.Access the values for day, month, year, and era. NSInteger day = [components day]; NSInteger month = [components month]; NSInteger year = [components year]; NSInteger era = [components era];4、监听本地化信息或者时区修改 可以通过NSCurrentLocaleDidChangeNotification监听区域的改变 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localeDidChange:) name:NSCurrentLocaleDidChangeNotification object:nil];同样的监听时区变化可以通过NSSystemTimeZoneDidChangeNotification。 从右到左语言的处理 创建从右到左语言的交互界面 支持从右到左向的语言,在约束层面应该使用Auto layout中的leading和trailing属性,而不是right和right。可以通过以下对比看到区别整体像是做了水平的翻转,有很多控件像是segmented控件, 进度指示器系统会自动做翻转。但是有些情况是不需要翻转的:视频控制和时间线指示器 图片,除非他们传达方向感,如箭头 时钟 乐谱 图表(x轴和y轴总是在一致的方向)获取布局的方向性 如果我们想获取当前语言是否应该是从右到左项的语言可以通过以下方法: //right-to-left language if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:view.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft) { … }设置文本的对齐方式 在iOS中默认的文本对齐方式是“natural”,在OS X中默认方式是“left”。natural的含义就是会感觉语言的方向自动调整为left或者right。 如果你想NSMutableParagraphStyle对象的对齐方式为自然的方向,可以: [[(NSMutableParagraphStyle *)paraStyle setAlignment:NSNaturalTextAlignment];对双向文本的处理 双向文本就是一段文本中及含有从右往左的文本还含有从左往右的文本。是不是感觉很诧异?因为即使像阿拉伯和希伯来国家这些书写习惯为从右往左,但是对于数字和拉丁文是从左往右写的。如果你使用的是标准控件,像Label,TextView,Textfiled他们会自动处理双向文本内容。如果你是使用自定义控件,那这些问题就需要你手动处理。 向双向文本添加Unicode标记 在某些特殊的时候,系统默认的行为可能会导致一些不正确的结果,这时我们可以通过添加Unicode标记进行纠正。 例如,手机号在所有语言中都是从左往右读的,如果一个需要本地化的字符串变量表示一个手机号,如果我们需要保证他是从左往右的顺序展示,需要再字符串首部增加一个从左往右的嵌入字符(LRE):U+202A,在字符尾部增加定向格式字符(PDF):U+202C。 // Wrap the plus (+) prefix and phone number in left-to-right directional markers NSString *phoneNumber = @"408-555-1212"; NSString *localizedPhoneNumber = [NSString stringWithFormat:@"\u202A%@\u202C", phoneNumber];翻转Cocoa Touch视图 有些视图是不应该翻转的,在iOS9之后可以通过UIView的semanticContentAttribute属性手动指定视图应该是从左到后还是从右往左的方式展示。 如果是想翻转图片可以通过UIImage的imageFlippedForRightToLeftLayoutDirection方法。 本地化测试 通过IB预览测试本地化 这个功能只能在.storyboard和.xib文件实现。选中preview之后我们可以通过其右下角的语言选项切换不同语言,然后我们可以实时观察调换语言之后的效果。 通过伪语言功能测试 通过Edit Schema > Run > Options 然后点开语言选项,除了各种系统支持语言外,翻到最下面可以看到这几个选项。1.Double-Length Pseudolanguage 可以将文本内容变成两倍长度。 2.Right-to-Left Pseudolanguage 将语言方向改成从右往左,也可以将语言改成阿拉伯文或者希伯来文。 3.Accented Pseudolanguage 带重音符号。 4.Bounded String Pseudolanguage 带边界的字符串。 5.Right-to-Left Pseudolanguage With Right-to-Left Strings 同从右往左语言。 引用 苹果文档:Internationzlization and Localization Guide
iOS开发月报#13|201907
- 01 Aug, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于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
- 29 Jun, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于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
- 03 Jun, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于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编写。是一个优秀的可供参考学习的开源项目。
将黑苹果系统升级至macOS 10.14.4(Mojave)
- 18 May, 2019
原有配置 操作系统: macOS 10.13.6 主板:AORUS MASTER z390 CPU: intel core i7-8700 显卡:NVIDIA GTX 750 内存:Geil 8G * 2 硬盘:tigo SSD 240G 如果要升10.14的系统,首先需要确认的是当前显卡是否支持。可以参照这个表Mojave硬件支持列表(持续更新中) 可以看到GTX 750已经无法驱动了,所以显卡要换。最好换成免驱的,我这里选了RX 560D。 注意事项 gtx750是即插即用,而rx560d有专门的供电线,所以更换显卡时一定要注意别忘了插rx560d的供电线。 升级准备 不管是装黑苹果还是升级黑苹果都需要有一个启动U盘,它可以在我们系统配置错误无法进入的时候,帮助我们通过U盘进入,然后我们再把配置改回来就行了。 制作启动U盘(大于8G) 1、插入U盘 2、打开 /Applications/Utilities/Disk Utility(磁盘工具) 3、选中U盘4、点击上方 Erase 选项按钮 5、你可以修改U盘名称 6、Format:选择Mac OS Extended(Journaled),中文对应:Mac OS扩展(日志); Scheme:选择GUID Partition Map,中文对应:GUID 分区映射7、点击Erase按钮8、下载UniBeast UniBeast版本要跟系统版本对应,需要注册tonymacx86账号才能下载。 9、安装UniBeast,需要把系统需要设置成英文才能进行安装。设置完毕,一路Continue。10、根据提示,选择Installation Type/Bootloader Configuration/Graphics Configuration然后完成,开始Copy系统文件。11、把MultiBeast拖进U盘 翻译自tonymacx86 升级Clover 如果想要安装macOS Mojave 10.14,它要求你的Clover Bootloader版本不低于r4515。最新的Clover版本可以在这下载。 升级系统 下载好升级程序之后,直接进行安装。会重启两次,之后是较长一段时间的等待(20-30分钟),跟正常macbook升级一样的流程。如果没有意外,那么恭喜你,黑苹果升级成功了。可能的问题 关机无法断电 这个问题网上有的说在config文件中的Acpi将FixShutdown选为true,有的说是增加电量修复的efi文件。我都试过,均无效,我看这方面的回答时间都比较久,应该是旧版本的解决方案。新版本只需要将FixShutdown制为false即可。 安装过程卡在最后2分钟或者卡在最开始18分钟 在EFI的drivers64UEFI文件中增加OsxAptioFixDrv3-64.efi文件即可。 完整的EFI文件,提取码:awxl。 如果因为配置出错无法进入系统 在BIOS界面选择U盘启动,即可通过U盘配置的EFI进入系统。然后更改正确设置即可。 重要文件记得备份Time Machine 免费 Carbon Copy Cloner ¥290.15 Super Duper $27.95Time Machine因为是苹果自带的功能,而且还免费,比较推荐使用这个。附一份教程 其他问题 当然配置黑苹果的机型组合有很多种,可能会遇到各式各样的问题。这里再贴几个可以参考的链接: macOS Mojave 10.14安装中常见的问题及解决方法 Hackintosh黑苹果驱动Clover
【译】iOS13新特性抢鲜看
原文链接 作者:MacRumors Staff 原文日期:2019-5-2初览 iOS13是苹果针对iPhone和iPad的下一代操作系统,将会在6月份的WWDC上和大家初次见面。传闻的功能包括夜间模式、iPad界面更新和新的主屏幕。我们可以期待什么 苹果位于加州的总部已经在开发iOS操作系统的下一个升级版本,该系统可以在iPhone、iPad和iPod touch上运行。 虽然目前我们对这款新软件的了解有限,但我们已经听到了一些有趣的传言,这些传言透露了一些我们有望在此次更新中看到的功能和变化,根据之前的更新,这次更新将被命名为“iOS 13”。iOS 12标志着苹果iOS开发政策的重大转变,工程师们现在有了更多的自由,可以在必要时推出尚未准备好的功能。 由于这个原因,iOS 13可能比之前的更新更加成熟,同时,任何未最终确定的功能都有可能被推迟。 由于苹果在iOS 12到iOS 13之间专注于bug修复和底层改进所引起的延迟,我们已经获取了惊人数量的泄漏信息,所以可以预见6月份iOS 13不少的特性。 我们可以预见的功能包括夜晚模式,音量外观的改变,iPad上关于多任务的更新,新的撤销手势,一个合并了“寻找手机”和“寻找朋友”的APP:“寻找我的手机”,闹钟和邮件的更新,以及像iOS app可以很容易的在Mac上使用这样的扩平台能力。 我们有望在6月份的WWDC首次一睹iOS 13的风采,届时将正式发布新款iPhone。开发者和开放的测试人员将更早一点使用到这次更新,beta版本系统测试期间,我们也会在MacRumors.com上持续跟进新的特性和改变。 可能的特性 夜间模式 iOS 13将首次拥有夜间模式,这个功能iOS用户已经期盼了相当一段时间了。这个夜间模式和MacOS Mojave里的夜间模式相呼应,它会会使用户拥有一个更好的夜晚观看体验。今年的WWDC里有一张夜间主题的效果图,可能正暗示了即将到来的iOS 13的夜间模式。iPad调整 iOS 13中将有几个特性是关于iPad的。据说,苹果将推出一项功能,可以在一个iPad应用程序中使用tab视图显示多个窗口。改进的多任务处理功能将在iOS 13中实现,iPad应用程序支持多个窗口和应用程序内的可堆叠卡。应用程序的特点是,最初附加在屏幕某一部分上的表格,可以通过拖动手势分离,变成一张可以操纵的卡片。 卡片可以叠在另一张上面,深度效果将指示卡片的上下效果,快速滑动卡片将使它消失。 新的手势 iPad或将拥有一个新的对于标准文本输入时的撤销手势,用户可以通过三根手指轻敲键盘区域,然后向左或者向右滑动,就可以撤销或者重做一个动作。 新的手势将允许用户在表视图和集合视图中选择多个项目,允许他们在项目列表上拖动多个手指来绘制选择,类似于Mac上的单击和拖动Finder。 音量指示器更新 iOS 13获奖包含一个新的音量指示器,它会比现在的指示器样式更缓和。 邮件 新的邮件app将把邮件内容分成市场、购买、旅游、不重要、更多等可搜索分类。此外还有一个稍后阅读的队列和一个针对特殊邮件线程的静音收件选项。 跨平台的iOS和Mac应用 在iOS 12和macOS Mojave的时候,苹果引入了一个新框架桥接iOS和Mac之间的应用,作为测试,像股票、家庭和录音等iOS独享的app被发布到macOS上。在iOS 13和macOS10.15, 苹果计划向开发者扩大这个功能,这将使得应用从iOS平台到Mac上的迁移更加容易。 新版“找到我的手机” 苹果正在开发一个融合“查找朋友”和“查找iPhone”的新应用,它很可能会随着iOS 13和macOS 10.15一同发布。该应用程序将包括一个新的“查找网络”功能,允许苹果设备被跟踪,即使没有连接到Wi-Fi或蜂窝网络,原理是利用附近的其他设备。 这个应用将包括已有的查找手机功能,像丢失模式、远程擦除设备。从“查找朋友”中提取的基于位置的共享选项也仍然可用。 据说苹果还在研究一个相关的硬件产品,像是瓦片一样的跟踪器。它被描述为一个“标签”,能够附着在任何设备上,并且通过用户的iCloud账号进行配对。当用户距离他们绑定的设备太远的话,这个小东西就会发出报警声,它是基于与iPhone之间的距离工作的。苹果没说什么时候发布它,但是有可能是跟新版iPhone一同发布。 地图 新版的地图应用将使哪些常用地址像是家或者公司的设置和导航更简单。频繁使用的地址信息将会更高效的分类,并且可以为他们配上图片。 提醒事项 一个新的提醒应用程序将在一个网格中包含四个默认部分,包括今天要完成的任务、所有任务、计划任务和标记任务。 图书 新版的图书应用将包含一个新的进度追踪功能和一个旨在鼓励用户阅读的奖励系统。 家庭 新的家庭应用将更好地与安全摄像头集成,并将提供一项无需第三方应用就能查看过去录音的功能。 健康 新的健康应用将有一个改良的每日活动视图和更加全面的月经周期跟踪。还会有一个“听力健康”的功能,它可以测量你耳机和周围环境的音量。 其他新功能 速度提升和bug修复 iOS 13和iOS 12很像,将会提高运行速度,并修复一个bug。 键盘 将会有一个新的默认的基于滑动的键盘可用,类似于SwiftKey。 字体 字体管理将在iOS 13中得到改进,设置应用程序将获得一个新的字体管理菜单。 更新分享栏 在iOS 13中,用于共享照片和web链接的共享表单界面将变得更加智能,这意味着用户可以向其中发送内容。 Safari 在iPad的iOS 13系统中,Safari会在必要时自动加载桌面版本的网站。苹果正在测试一款Safari下载管理器,用户可以在一个地方下载。 照片实况更新 苹果计划将照片实况的视频长度延长至原来的两倍,也就是从3秒变成6秒。 嗨Siri ”嗨Siri“将会更好的过滤像是笑声和孩子哭声这种环境音。 屏幕使用时间 屏幕使用时间功能将增加一个新功能用于限制孩子对手机的使用,可以设定一个可以和不可以玩的时间。 界面的更新 当启动多任务处理面板并关闭应用程序时,将会有一个新的动画,iPad的主屏幕将会发生一些变化。 文件应用的修改 虽然关于一个改版后的文件应用程序会是什么样子的细节很少,但据说苹果正在为它开发新的功能,比如更好的第三方软件集成。 新版的Emojis 新表情符号将不会在iOS 13发布时出现,但在2019年晚些时候的iOS 13更新中,苹果将会引入他们。Unicode联盟已经确定了加入表情符号标准的字符,其中包括火鸟、水獭、华夫饼、树懒、白心、牵手的人、冰块、潜水器、猩猩、果汁盒、沙拉三明治等等。辅助功能 一个更全面的辅助功能菜单将出现在设置应用程序的主页上,其中包括改进的助听器支持等。 iPad Pro支持鼠标 有传言称,苹果公司可能计划在iPad Pro上添加USB鼠标支持,作为一项辅助功能,而不需要使用适配器。 据报道,iPad Pro的USB-C接口将允许你插入USB鼠标,作为那些无法使用触摸屏的用户的另一种输入方式。目前还不清楚该功能何时会实现,但它最早可能在iOS 13中实现。 跟开发者相关的变化 对于开发者来说,iOS 13将为媒体播放、搜索、语音呼叫、活动票务、消息附件、航班等功能带来改进的Siri集成。 其他面向开发者的功能还包括ARKit的改进,它为增强现实提供了一个全新的快速框架,以及一个配套的应用程序,让开发者可以在视觉上创建增强现实体验。ARKit还将支持游戏控制器和立体声AR耳机。 新框架将包括扩展使用Taptic引擎,对第三方应用程序的文档扫描支持,以及无需使用照片应用程序即可从外部设备捕捉照片的功能。 NFC将得到改进,开发人员将能够在他们的应用程序中添加对扩展的NFC格式的支持,并且还将添加CoreML的更新版本。
iOS开发月报#10|201904
- 30 Apr, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于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招聘信息
今天放出一波我司的招聘信息吧,这里只说了iOS的招聘要求。其实还有Android + Unity + 游戏测试等一大波岗位虚位以待,如果有意愿的小伙伴可以私信我哦。公司介绍 乐信圣文(Learnings)是一家全球领先的移动出海应用开发商,致力于未全球用户提供卓越的移动应用。公司成立于2016年2月份,是行业成长最快的企业之一,是Google,Facebook和Apple的全球战略合作伙伴。 截至2018年11月,公司产品在全球移动端月活用户超过2000万,累计用户超过3亿;其中70来自欧美地区。 团队规模:150人 公司地址:海淀区东升科技园(地铁8号线西小口站) 明星产品-Peace冥想【App Store 2018年11月编辑最爱】 【App Stroe 首页《生活解决方案》专题推荐】 【App Store 健康健美分类编辑推荐】 "Peace"冥想是一款你的负面情绪排解神器,随时随地帮你在短短5分钟内与自己和解。助眠,减压,它是都市快生活下的一剂良药。 Peace上线不到一年,就表现出了巨大的成长潜力,它的未来将会更加灿烂。 任职要求职位: iOS中高级开发 薪资: 1530k + 年终奖(16个月薪资) 任职要求:我们期望你有3年以上开发经验,当然如果你能力够强,这条可以忽略。 对数据结构、面向对象、设计模式有一定的认识 具备独立开发一款产品的能力 乐观、主动、具备团队合作精神福利待遇双休(非996!) 一日三餐 运动健身(室内台球、乒乓球每周篮球训练) 顶配Macbook Pro + 升降办公桌 零食下午茶 节日福利 不打卡 ...欢迎你的加入 有没有心动呢,心动不如行动,有兴趣,有需求的小伙伴赶紧把简历砸过来吧! 我们有各个技术栈的大牛,可以让你进步更快;我们敢于尝试新技术,一年前就已经使用纯Swift开发项目;我们重视人才,只要你够牛,薪资+年终奖拿到你手软。 来到公司之后,我们将一起经营维护Peace这颗新星产品,一起见证它的成长吧。
iOS开发月报#9|201903
- 01 Apr, 2019
这里记录过去一个月,我看到的值得分享的内容,包含但不限于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%用于风险什一税--实际上我们把这部分收入捐给一家慈善机构,以帮助印度的孤儿。在我们第一次奉献什一税的第二周,我的妻子就得到了加薪,加薪的数额正好是我们当时奉献什一税的数额。我个人认为,我们的成功很大一部分就是因为这种对奉献的承诺,一直恪守到今天。 即使你不信仰任何宗教,我认为这一点也有某种符合逻辑的解释。我认为,你把钱看的越重,你就越难以在理财方面做出明智的、成功的投资选择。自愿把自己收入的固定数额奉献或者捐赠给慈善机构,可以改变你对金钱的看法。这一思想上的转变让你从金钱的所有者变成管理者。 --《软技能:代码之外的生存指南》