最初发布于 swiftrocks.com 。 竞争编程是掌握特定编程语言的一种好方法。 即使您对参加诸如Facebook Hacker Cup之类的世界大赛不感兴趣,仅使用语言的基础知识来解决棘手的算法问题,也会使您面临该语言所没有的方面/捷径,例如效率某些方法/操作以及如何编写更好的替代方法。 这是一个很酷的爱好,而且,作为奖励,您甚至可以间接地为自己备受恐惧的Big 4采访做好准备,在这些采访中您必须在白板上解决算法问题! 尽管在真正的竞争中,使用Swift编码会造成很大的麻烦(因为与其他语言相比,它有点慢,并且几乎没有本地数据结构),但是许多流行的在线平台(如LeetCode,HackerRank和Codefights)都支持Swift,它们是一种很好的方式以使自己成为更好的iOS开发人员,特别是因为竞争问题只会接受非常快速的算法作为可能的解决方案。 如果您以前从未这样做过,我强烈建议您尝试一下。 在本文中,我将为您提供有关如何使用Swift读取输入数据的简短教程,以及一些解决Swift中竞争问题的技巧。 使用Swift读取标准输入 在竞争性编程中,问题的输入通常被馈送到命令行应用程序,该命令程序对此有所了解并输出所需的结果。 诸如LeetCode和CodeFights之类的平台将自动为您处理输入数据,并公开一个空的Swift方法,该方法应返回问题的解决方案,但是其他平台(如HackerRank)有时会让您手动读取,解析标准输入然后打印结果。 幸运的是,这并不像听起来那样复杂。 要模拟竞争性编程环境,请在Xcode中创建一个命令行工具项目: 请注意,命令行工具不是Cocoa应用程序,因此您将无法访问UIKit东西! 像Foundation这样的核心框架就在这里。 在main.swift ,键入并运行以下代码: let line = readLine() print(“Got something! \(line)”) 此时,程序将被冻结。 readLine()是一种标准库方法,可以同步读取标准输入并返回String? 一旦检索到整行,或者如果达到EOF,则返回nil 。 如果您在Xcode的控制台中键入内容,程序将继续执行并打印您编写的内容: 但是,竞争性编程问题可能需要数百行输入。 您可以使用while循环使代码运行直到输入结束: while let line = readLine() { print(“Got something! \(line)”) } 这就是您开始需要了解的所有信息。 从现在开始,您使用纯Swift的技能是唯一的变量。 如果您以前从未这样做过,这是一个示例问题: 示例问题 给定一个整数数组,找到其元素的总和。 输入格式 第一行包含一个整数,表示数组的大小。 第二行包含n空格分隔的整数,它们表示数组的元素。 输出格式 将数组元素的总和打印为单个整数。 […]
使用Xcode 10,在构建Swift项目时,性能和开发人员的工作效率有了很大的提高。 构建时间很无聊,如果构建时间更长,可能会分散开发人员的注意力,Apple付出了一些努力来优化构建时间,尤其是对于Swift语言。 基本上,构建时间取决于所使用的代码行,代码行数,依赖性和机器配置。 Xcode 10和Swift编译器今年都宣布了各种新功能,从而使Swift构建比以往更快。 这个GitHub存储库中已经涵盖了各种技巧,但是我们将介绍Xcode 10和Swift 4.2中的新增内容,这些内容将有助于我们更快地构建Swift。 1.了解Xcode的构建过程 构建过程是使用一些本机命令行工具(例如swiftc,clang,ld,actool,ibtool等)在后台执行的任务的集合。它涉及编译和链接源代码,复制资源(如资产,StoryBoards,代码)使用构建脚本等进行签名和自定义。 但是,Xcode的构建过程可以有效并以正确的顺序自动执行这些任务,因此我们可以加快构建过程。 如果可能,可以并行完成Swift源文件的编译,以便链接器可以一次执行所有这些任务,以准备应用程序的可执行文件。 Xcode构建系统负责分析依赖关系并确定可以并行运行的任务。 理解增量构建的概念也很重要,这样,如果不进行更改,就不需要构建所有东西。 通过观看此WWDC会话,您将了解有关Xcode构建过程如何在后台工作的更多信息。 为了加快构建过程,我们应该通过识别和优化应用程序中的依赖关系来帮助构建系统更快地完成其工作。 这就是为什么了解Xcode构建过程如何在后台运行很重要的原因。 2.使用新的构建系统 Apple在Xcode 9中启动了新的构建系统,但默认情况下未激活它。 但是,对于Xcode 10,默认情况下会激活新的构建设置,并从Xcode文件->项目/工作区设置中启用 查看有关Xcode新构建系统的先前博客文章,以获取有关构建系统的详细信息。 如果要使用xcodebuild从命令行构建iOS项目,那么我们必须传递附加参数-UseNewBuildSystem = YES还将强制使用新的构建系统。 新的构建系统称为xcbuild。 苹果xcbuild的二进制文件位于路径中 /Applications/Xcode.app/Contents/SharedFrameworks/XCBuild.framework/Versions/A/Support/xcbuild 3.并行化Swift构建 Xcode项目由多个目标组成,一个目标可能依赖于另一个目标来构建。 Xcode目标指定了要构建的产品,例如,对于主应用程序和单元或UI测试,我们可能有不同的目标。 当一个目标依赖于要构建的另一个目标时,它会创建依赖关系,例如对iOS应用程序的单元测试,我们首先需要构建主应用程序,然后才能构建单元测试目标以执行单元测试。 在Xcode构建阶段中,我们可以使用Target Dependencies显式指定目标依赖关系,并隐式使用Link Binary with Libraries 串行构建目标将需要时间,并且可能不利于利用系统资源。 如果可能的话,建立目标真是太棒了。 借助Xcode 10,我们可以并行化构建并分析引擎盖下的依赖关系,从而显着增加了构建时间。 我们可以通过编辑Xcode方案并在方案的构建动作中检查并行构建来启用并行构建。 确保同时检查了Parallelize Build和Find Implicit Dependencies,以确保我们从Xcode中获得最佳性能。 并行化构建将确保在可能的情况下并行运行构建,并且Find Implicit依赖项将检查项目内部的所有依赖项,通常在“将二进制文件与库链接”构建阶段。 Xcode 10还引入了“并行目标构建过程”的功能,这意味着从属目标可以尽快开始编译而无需首先等待完成,但是它必须等待第一个目标的“运行脚本”阶段。 4.改善运行脚本阶段 在Xcode的构建阶段,我们可以添加自定义运行脚本来根据我们自己的项目需求进行构建过程。 很好的例子是Carthage,如果您使用Carthage进行依赖项管理,那么您必须经历过运行脚本阶段。 […]
这是WWDC 2017演讲的简短表格摘要,仅重点说明要点和关键示例: UIKit应用程序中面向协议和价值的编程– WWDC 2016 –视频– Apple Developer 在去年的面向协议的编程和具有值类型的会议上构建更好的应用程序的基础上,今年的…… developer.apple.com 您可以从WWDC视频中下载完整的示例代码: https://developer.apple.com/library/content/LucidDreams/Introduction/Intro.html#//apple_ref/doc/uid/TP40017334 本地推理-关于功能的独立思考。 您不必了解应用程序的整个上下文即可维护各个功能。 您应该只能够应用局部推理来理解各个功能。 MVC:模型存储您的数据。 视图将显示它。 控制器在两者之间进行协调。 使用Structs之类的Value类型与Classs之类的引用类型相比,可以促进局部推理,简化关系,并使代码更具可测试性。 结构在其所有成员上赋予值语义和独立存储。 在模型层中使用值类型是标准且无争议的。 但是,一旦您开始在其他地方(例如控制器)使用值类型,人们通常会犹豫不决,或者不知道该怎么做。 例如,子类化一个tableview单元是有限的,因为您不能在不是单元格的其他视图中重用该代码。 代替使用UITableViewCell子类,我们可以将其更改为结构并实现layout(in rect:CGRect)方法: 然后,我们更新单元格子类以在上述布局结构中调用图形调用。 现在,我们还可以从其他区域调用此绘图代码,而不仅仅是表格视图单元格: 通过为布局代码提供样本以进行内部布局,然后验证生成的框架,也可以更轻松地对布局代码进行单元测试。 我们不必考虑该视图使用或替代的其他特定视图功能。 现在,我们可以更改布局对象以使用协议,而不是引用UIViews。 这样我们就可以将布局逻辑也应用于其他类型的对象(例如Sprite Kit对象),而不仅仅是UIView子类。 下面我们还使用“ Retroactive Modeling”使UIView和SKNode都符合我们的布局协议: 协议是通过继承实现多态性的绝佳解决方案,因为您可以将相同的额外行为应用于任何不相关的类型。 当前,“内容”和“装饰”都可以是任何类型的对象,并且它们不必是同一类型。 如果我们想将两个约束为相同类型,可以使用泛型通过在结构上指定所需的类型来实现。 (现在,内容和装饰都必须与传入的Child类型相同): 使用泛型还可以为编译器提供有关代码正在执行的操作的更多信息,并允许我们进一步优化代码以提高性能。 继承牺牲了“局部推理”,因为您必须考虑父类在做什么,它如何工作,将要更改或覆盖的内容以及如何使其与父行为一起工作。 如果要继承系统对象,那么这些对象也将非常复杂。 它使您的思维在父母和班级之间跳来跳去,系统变得更难以理解。 合成意味着将许多较小的片段组合在一起,以制成较大的片段。 类很昂贵,会导致堆分配。 使用类来构成视图非常浪费。 结构更便宜,并且具有有价值的语义(因此具有更好的封装性)。 没有人可以修改您的副本。 您可以使用以下结构为不同的布局类编写视图组成代码: 但是,仍然存在更好的方法,只需简单地使布局结构也符合布局协议即可。 (从它们两个中都删除冗余方法布局): 现在,我们的视图和布局都使用相同的Layout方法,将自己定位在给定的rect中是他们的责任。 合成使我们能够以声明的方式构建高级布局: 如果我们希望布局以正确的顺序相互嵌入视图(层叠),则每个布局都需要以正确的顺序将其子级返回给其所有者。 因此,我们可以在内容上实现get方法,并让每个实现者返回其所有视图。 […]
最近,我一直在iOS中使用“文件”,与Android相比有点复杂。 因此,在这篇文章中,我想介绍如何为用户从我们的应用程序预览,打开,共享,保存或打印来自远程URL的文件 。 首先,这是您需要从Apple提供的UIDocumentInteractionController文档中了解的内容。 使用此类提供用于预览,打开,复制或打印指定文件的适当的用户界面。 显示其用户界面后,文档交互控制器将处理支持文件预览和菜单显示所需的所有交互。 您还可以使用委托来参与所显示界面中发生的交互。 在文件可以出现在“文件”应用中之前,您必须指示您的应用支持“ 就地打开和已启用文件共享” 。 这些选项是使用Info.plist文件中的键配置的。 第一个键是UIFileSharingEnabled ,它使iTunes可以共享“文档”文件夹中的文件。 第二个键是LSSupportsOpeningDocumentsInPlace ,它授予本地文件提供者对Documents文件夹中文件的访问权限。 将这些键添加到Info.plist并将其值设置为YES 。 UIFileSharingEnabled:应用程序支持iTunes文件共享 LSSupportsOpeningDocumentsInPlace:支持就地打开文档 在转到代码之前,这里需要了解一些UIDocumentInteractionController实例属性和方法。 实例属性 url标识本地文件系统上目标文件的URL。 ( 请注意, url属性需要本地文件系统上文件的URL,而不是 文件 的远程URL ,即“ https://…”。如果您仍然提供远程URL来测试会发生什么,请告诉我,应用程序将崩溃 ***由于未捕获的异常“ NSInternalInconsistencyException”而终止应用程序,原因:“ UIDocumentInteractionController:无效的方案https”。 仅支持文件方案。’*** 2. uti目标文件的类型。 此属性的值用于确定哪些应用程序能够打开文档。 3. name目标文件的名称。 此属性包含没有任何先前路径信息的文件名。 此属性的默认值是从url属性中的路径信息派生的。 实例方法 presentPreview(animated :)方法 此方法异步显示文档预览。 文档交互控制器响应于适当的用户交互而自动关闭文档预览。 您也可以使用dismissPreview(animated:)方法以编程方式关闭预览。 情况1:假设您在远程URL上有一些文件,您想为用户下载该文件,并允许他们在他们希望的任何地方存储/共享。 请注意,用户iPhone中所有支持目标文件类型的应用程序都将显示在操作表的应用程序列表中。 用户可以将文档保存到iCloud,投递箱,本地文件,相册(如果支持目标文件类型)或他/她希望的任何位置! 😃 情况2:假设您在用户iPhone的某些应用中获得了一些文件,您希望他们在他们希望存储/共享的任何地方 在上面的代码中,您不需要在tempURL中存储任何内容,您只需要在url属性中提供适当的文件路径,为“ com.adobe.pdf”更正uti和文件名即可。 然后,使用presentPreview(animated:)方法,您可以轻松进行👍🏼 为了撰写这篇文章,我从网上的一些文章中获得了参考。 […]
SmartBaby是一个Mac应用程序,可帮助您从JSON推断Swift Codable模型以节省时间。 如果您有一个响应为JSON的API,则可以使用SmartBaby节省大量时间来创建模型类型。 只需将JSON粘贴到SmartBaby,它将推断并生成Codable模型供您复制。 使用右侧的选项面板,您可以更改有关模型的一些元信息,甚至可以使用功能强大的地图来自定义生成的模型。 请注意,SmartBaby会将嵌套的JSON推断为嵌套模型。 另外,如果可以,SmartBaby将推断字符串为URL(或日期)。 您可以更改模型的名称,添加初始化程序,更改twitter的id的类型,使url为可选等等。 玩吧。 在左侧的列表面板中,您可以检查所有模型。 通过更改模型的“ 项目名称” ,您可以将它们与项目组进行组织。 如果模型太多,请按模型名称搜索以找到所需的模型。 您所有的模型都将通过CloudKit保存到私有数据库中的iCloud。 您几乎需要了解有关SmartBaby的所有信息。 您现在可以在Mac App Store上获得它。 https://itunes.apple.com/us/app/smartbaby/id1438971945?mt=12 您可以为它评分或在使用后对其进行评论。 🐣 如果喜欢,请推荐给您的同事或朋友。 🔥 欢迎反馈! Twitter:@nixzhu,电子邮件:zhuhongxu@gmail.com
在类和结构之间进行选择实际上可能会对您的代码产生巨大影响,或更具体地讲,从技术上来说是不正确的。 确保您可以做得更好! 先决条件: 面向对象基础 基础 struct是值类型,而class是引用类型。 困难在于,对于许多用途而言,类和结构是可互换的。 我们将依次轮流使用它们的“预期用途”以及轮流使用的一些基本功能。 值和引用类型的定义如下: 简单的结构 结构可以封装数据值,因此可以合理地认为这些值将在项目周围复制而不是被引用(由struct,Int,Float,Array等存储的所有属性都是值类型)。 当您免费获得默认初始化程序时,它们特别易于使用。 由于结构是值类型,因此它们保留在堆栈中 。 复制值类型会创建两个名称不同的值类型实例(此处为struct)。 构造图是免费获取初始化器的,您可以在其中使用免费初始化器初始化所有属性(即使它们已预先初始化)。 简单的课 上面结构的等效类必须具有一个自定义初始化程序。 类获得免费的初始化器,但前提是属性具有预初始化的值。 要注意的一件事是,值类型存储在堆栈中,但是如果类具有结构的实例,则该结构将与类一起存储在堆中。 什么时候使用类或结构呢? 在身份重要的地方使用一个类,在平等重要的地方使用一个结构 和… 无法继承结构 好吧,这很有意义(线索就在名字中),因为结构是值类型。 但是您可以扩展一个结构和一个类 实际上,您也可以扩展枚举! 值类型是线程安全的 您可以信任跨线程使用值类型,因为它们正在使用原始类型的副本。 这样可以避免对引用类型使用锁定。 值类型表现出色 值类型比引用类型快。 这是因为引用类型是在堆上分配的,而不是在栈上分配的。 可以比较引用类型以查看实例是否完全相同 ===可以确保两个对象相等,这意味着它们存在于同一内存地址。 逃生内存泄漏地狱 结构不是按设计使用引用,这使它们不受内存链接的影响。 大! 你知道吗 Swift主要使用值类型。 与目标C相反! 资源资源 https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
打开Xcode并创建一个新的Cocoa Touch框架或Cocoa Touch静态库。 我们称之为: MyFrameworkProject 为此,您需要使用要在Playground中测试的库创建Podfile,例如在此处Alamofire: 来源’https://github.com/CocoaPods/Specs.git’ use_frameworks! 目标’MyFrameworkProject’做 pod’Alamofire’,’〜> 4.7′ 结束 然后运行: 吊舱安装 打开使用cocoapods创建的工作区并创建一个新的游乐场。 要白蚁,请在工作区中添加游乐场。 现在,您可以使用新创建的游乐场内的第三方库(此处为Alamofire)。 导入 UIKit 导入 Alamofire var str =“你好,操场” 您可以在这里找到代码:https://github.com/barrault01/cocoapods_insideplay_ground
在Swift中,可选项被实现为包含一些或不包含任何枚举的枚举。 先决条件: 对枚举和相关值有一些基本了解 术语 可选:Swift引入了可选,它们通过声明是否存在值来处理缺少值的情况。 可选是一种类型。 枚举:枚举类型是由一组类型的成员组成的数据类型。 整数可选的正式版本 这里的想法是,我们将尝试在Swift中如何实现可选方法。 因此,在Swift中,我们可以使用以下内容创建可选的Ints: 在Swift中实现Integer可选 因此,可以通过枚举(具有关联值)实现可选选项。 没有值时将其称为“可选”不是理想的选择,而是与内部实现匹配的。 对字符串或字符执行相同操作很简单。 但是,最好使用泛型来生成泛型可选 在Swift中实现泛型可选 Swift中的泛型实现通常不会出现问题,幸运的是,它也不是: 一个Swift文件发布在GitHub上:https://github.com/stevencurtis/implementoptionals/blob/master/main.swift 想联系吗? 在此处尝试链接: 史蒂文·柯蒂斯(@stevenpcurtis)| 推特 史蒂文·柯蒂斯(@stevenpcurtis)的最新推文。 在开发iOS应用程序的同时学习计算机硕士学位… twitter.com
你曾经没有冰箱住过吗? 自从我们一个多月前破产以来,这就是我一直在做的事情。 原来,我们的厨房在冰箱周围进行了翻新,所以把破损的冰箱搬出来是一场噩梦。 整个经历使我开始思考我们在多大程度上依靠我们的设备为我们做不同的事情。 当我们第一次买到冰箱时,我真的很兴奋,因为它也有一个饮水机。 因此,现在,我们的冰箱不仅用于储存食物并保持冷藏,还可以用来取水。 我们已经有一个饮水机,我们的厨房水槽。 我们用它来洗碗,但是我们也有一个洗碗机。 我们有许多不同的设备,它们以不同的方式完成相同的任务,以完成各种任务。 作为一个新手程序员,我一直在思考如何在代码中表示事物。 通常,在编写代码时,我的理念是问“完成此任务看起来像什么? 涉及的机制和步骤是什么? 所以预期的条件和结果是什么?”因此,当我了解到所有可以做的事情(例如Swift)时,我会以类比的方式来使用它们来解决生活中的问题。 所以我有一个装满电器的厨房,我想用代码表示它们。 传统上,使用面向对象编程,我将为每种通用设备类型定义一个类,然后具有特定的子类来共享其大多数方法和属性,并添加自己的独特方法和属性。 太好了,但这就是问题所在:就像一个程序一样,我的厨房经历了无数次迭代。 如果我在12月为我的厨房编写代码(我将其称为以前的版本为Kitchen 2.0),因为我的冰箱分配了水并且也是唯一使食物保持低温的东西,也许我会先定义Sink类,然后才有了冰箱包括一个水槽对象以允许其分配水。 我本来让洗碗机继承了洗碗的能力,却没有分配水的能力。 好吧,现在冰箱坏了,我们有一个迷你冰箱。 那根本不分配水。 因此,如果我要更新厨房代码,则必须为不继承水分配功能的迷你冰箱增加一个新类。 好吧,这就是这样的大型项目的本质。 但是,当我们买到新冰箱时会怎样? 技术在10年中取得了长足的进步,现在出现了一种全新的设备,即“智能设备”,可以访问互联网。 在OOP范式中,从经过翻新的Kitchen 2.o到智能电器Kitchen 3.o,将是一项疯狂的工作。 我必须决定是否重新分类几乎所有内容,使冰箱成为智能设备和冷却设备的父类,是否要从小型冰箱继承还是成为其父对象,还是要创建新的智能设备?反对并将其纳入我的课程。 更改父类时,我必须返回并确保所做的更改没有破坏任何子类。 我必须进入每个子类,并确保我要考虑其内部方法中对父类代码的任何更改,并确保我覆盖了我不想继承或不想继承的任何方法。功能有所不同。 整个过程将花费一些时间来实现我真正兴奋的超酷“智能设备”功能。 Kitchen 3.o更新看起来是一场巨大的噩梦。 此外,您可以看到继承如何在其头上代表其自身的概念基础。 通常,您会想象一个简单的对象从更复杂的对象继承其功能。 但是,继承模型具有从简单对象继承的更复杂的对象。 这会导致更改从简单且易于修复的对象向外过滤到复杂且难以修复的对象中。 这就是为什么我对面向协议的编程感到如此兴奋的原因。 协议旨在概述对对象的要求,以便它们可以与其他对象安全地交互。 但是从我目前的厨房状况来看,我可以看到POP的多功能性对于真正实现OOP设定的目标将有真正的帮助。 我可以通过使用Swift的协议从传统的继承模型切换到模块化(面向方面)模型来实现。 它将使Kitchen 3.oa变得束手无策! 在以协议为中心的模型中,我可以将可用于多个类的所有常用方法划分为协议。 协议将允许我定义几个类共有的功能,并对操作它们所需的变量实施要求。 在下面,您可以看到如何使用协议在Swift中实现我的Container模块。 我使用“ _m”来区分我的模块和其他类型的协议。 您可以看到我正在设置变量“ storedItems”的要求。 我需要定义此变量是否可以直接访问,并且如果我将Structs与此模块兼容,则需要定义将其修改为Mutating的方法。 为了允许类使用模块,我需要像普通协议一样将其添加到类声明中。 现在,该类的任何实例都可以使用它从模块中获得的方法。 我不必声明这些方法即可编译我的代码,如果需要添加功能或完全替换它们,也可以覆盖它们。 […]
最近开始有机会用到UIcollectionView,就来做一下笔记吧,个人本来认为它跟UItableView蛮像的,但实际做起来还是有一些区别。 直接开始吧!先拉个集合查看到故事板上面并设置好约束, collectionView左上角有内建一个cell,在这里不会用到所以可以直接删除掉,或者在下图右边的Collection查看项目将1改成0。 拉好后马上设定delegate,在文档大纲视图中,选择集合视图再重新按下ctrl +左键拖曳到viewController,选择delegate,再重复一次选择数据源。(当然用代码设定也是可以,看个人喜好) 接下来要设定我们想要自定的cell的格式,我本来是使用内建的cell再去修改的,但后来发现xib似乎比较好用,所以就用xib来创建cell,也可以顺便学一下xib 。 先New一个Cocoa Touch Class,子类为UICollectionViewCell,XIB那栏要打勾。 创建好之后就会多出xib档案和swift档,那先来设计我们的cell吧。先在cell里面拉一个视图出来。 设定View的约束条件,这边宽度要记得重置因为后面会用到,AspectRatio我也有勾。 接下来再把需要的元件拉进去并排好位置 再来设定各元件的约束,设定完后可以调整视图的背景色,可以帮助理解单元格的正确显示区域。 再来单击最上层的我的收藏夹视图单元格,在右边显示属性检查器的第一个收藏夹可重用视图栏位填上自己设置的标识符名称,像这边设定成myCell的话,文档大纲视图的我的收藏夹视图单元格就会改名成myCell。 接下来要建立xib和myCollectionViewCell.swift档案之间的连结,在该档案加入以下的代码: 到这里就完成xib的设定了,此时应该会会想要忍不住Run一下程式,但很不幸的crash了,这是因为viewController那边还有很多地方未设定。 回到ViewController.swift,给StoryBoard上的收藏视图设置ㄧ个插座 iPhone SE 大尺吋平板和最小手机都试过了,经过以萤幕宽度的计算,来达到一次显示两行单元,而且大小会自动缩放,那其他型号应该也没问题了吧! 范例档网址: ttyUSB0978 / customCollectionViewTest customCollectionViewTest – https://medium.com/@ttyUSB0978/ios-swift-custom-uicollectionview-880df6abcc51 github.com 参考资料: UICollectionView具有在iOS 9和10中使用自动布局自动调整单元格大小的功能 相关资料: WWDC2016会议笔记— iOS 10 UICollectionView新特性 Swift —圆形图片的生成及显示(两种方式) Swift:用UICollectionView整一个瀑布流 PS。 demo用的图片是我之前在网路上收集的,如果作者看到觉得不OK请通知我,我再换掉