Tag: 软件开发

iOS上的事件传递:第2部分

在本系列的第一部分中,我们讨论了触摸处理。 现在,让我们更深入地研究“响应者链”的工作方式,并查看可在iOS应用程序中处理的其他类型的事件:运动事件,远程控制事件和按键命令。 有关响应者链的更多信息 我们在介绍触摸事件时简要概述了响应程序链,但让我们仔细看一下。 响应者链是在整个应用程序中传递这些其他类型事件的主要机制。 响应者链的概念很简单。 它由UIResponder对象链组成。 链中的每个元素都有机会响应给定的消息。 如果给定的响应者不响应该消息,则链中的下一个项目将有机会进行响应。 如果链中没有对象响应该消息,则该消息将被丢弃。 第一个有机会响应消息的对象称为“第一响应者”。第一响应者是最近调用-[UIResponder成为FirstResponder]的对象 。 通过覆盖-[UIResponder canBecomeFirstResponder]并返回YES,对象可以成为第一响应者。 当对象作为第一响应者完成时,调用-[UIResponder resignFirstResponder] 。 作为动作事件和远程控制事件的响应者,非常简单。 作为第一响应者的对象被发送了运动或远程控制消息。 然后,它就有机会响应事件,将事件发送到响应者链,或者通过处理事件然后在超类上调用方法来完成。 涉及的方法在UIResponder中定义。 Apple的UIResponder类参考是获取更多信息的重要来源。 在实践中 我们可能会问,“这一切都很好,但是我可以在实践中使用它吗?”让我们来看一个示例应用程序。 首先,请克隆GitHub存储库。 该应用程序不是很有用,但是它演示了响应程序链如何工作的简单示例。 它需要一个硬件键盘,并且只能做一件事:在灰色框中打印字母“ a”。 它可以在模拟器或带有键盘的任何iOS设备中正常运行。 让我们深入一些代码。 首先,让我们看一下主视图控制器: 类ViewController:UIViewController { @IBOutlet弱var customTextView:CustomTextView! @IBAction func textViewTapped(_ sender:AnyObject){ customTextView.becomeFirstResponder() } } 这个视图控制器很简单。 它包含CustomTextView类型的自定义视图,并处理在情节提要中配置的轻击手势识别器的操作。 轻击手势处理程序在customTextView上调用beginFirstResponder 。 现在让我们看一下CustomTextView 。 类CustomTextView:UIView { 私人var textToRender:NSString吗? { didSet { […]

我让我的iOS项目陷入混乱。

不久前,我休了几个星期的假期。 作为首席开发人员,我对团队充满信心,并知道所有团队成员都是经验丰富的开发人员,并且对项目本身有丰富的知识。 他们中的大多数人比我拥有更多的语言经验,并且所有人都在使用TDD进行工作。 该项目掌握得很好。 欢迎回来! 我同样充满信心地回来了,很高兴看到项目经理和产品负责人对团队所做的工作感到满意。 团队很高兴地告诉我,一切进展顺利,他们提供了所有预期的功能,甚至开始从事一些期待已久的技术任务。 愿景很简单: 文件无处不在 。 我打开了这个项目,意识到了以前错过的一切,为什么我的头脑不明白发生了什么。 该项目的组织无处不在。 名称难以理解的文件夹,来自项目层次结构中同一级别上不同层的对象文件。 对于这个项目的新手来说,根本不可能理解该看哪里。 我开始了解发生了什么,但还不确定为什么会发生。 直到我问了一个关于两个特定类的角色的问题,这两个类应该是同一类的,得到的答案是: 好吧,是的,该类以这种方式工作,但是另一类则根本不以这种方式工作。 我相信,即使您有时在性能上有所损失,但优势仍然太大,以至于您无法在域元素周围阐明模块。 人脑只能弥补很多东西,而当功能实际上被分为多个模块时,很容易就看不到您实际在做什么。 您的项目应始终反映您所做的设计决策,并且应明确您的工作内容。 如果您喜欢这篇文章并认为其他人可能会感兴趣,请随时分享并给予一些鼓掌。 我计划写更多文章,所以 如果您想了解下一个内容,请在这里 关注我 。 您也可以在 Twitter 上 关注 我,讨论任何事情。

Swift 4.2中的委派模式

什么是协议? 根据 Apple文档 – 协议定义了适合特定任务或功能的方法 ,属性和其他要求的蓝图 。 然后,该协议可以由类,结构或枚举采用,以提供这些要求的实际实现。 满足协议要求的任何类型都被称为符合该协议。 除了指定必须符合标准的类型的要求之外,您还可以扩展协议以实现这些要求中的某些要求,或者实施符合标准的类型可以利用的其他功能。 在SwiftTable语言中开发UITableViewDelegate和UITableViewDataSource基本表视图时,您已经看到了很多次的协议 。 两种协议都有其自己的方法是必需的和可选的。 如果您已经在Apple的框架中看到过。 如果您按命令键( ⌘ )并单击鼠标左键,则在 UITableViewDataSource上 可以看到这些选项– 现在单击“跳转到定义”,您将看到他们的协议方法–

开闭原则:正确扩展实体

在SOLID首字母缩略词“封闭式原理”中包含的所有设计原理中,可能是最难理解且最经常破解的。 毕竟,如果您想向实体中添加功能,为什么不直接将其添加到实体的现有实现中呢? 作为新手程序员,我肯定会感到这种感觉。 本文的目的是首先讨论“开放式封闭原则”背后的原理,然后研究三种正确扩展实体的技术。 什么是开放式封闭原则,为何如此重要? 让我们从Bertrand Meyer最初编写的定义开始: 软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改 本质上,我们是在谈论以某种扩展方式向实体添加任何新属性或方法,而不是将它们直接放入实体中(然后进行修改)。 那是有道理的,但是为什么我们要在这个麻烦上走? 这是一些原因… 遏制错误的引入 假设我正在编写一个供第三方使用的软件包,并决定要向现有实体添加一些新功能。 从表面上看,这似乎很好。 但是,当我进行这些更改时,我并未考虑系统中其他实体与我刚刚修改的实体进行交互的所有不同方式。 我的修改会产生意想不到的影响,导致系统其他部分出现错误吗? 通常,我们希望逐步开发我们现有的代码,以使其尽可能高效且无错误。 在此过程中引入新的属性或方法可能会取消数月甚至数年的迭代改进。 单独关注 当我向现有实体介绍新功能时,很可能会打破SOLID原则中的不止一项。 如果新功能使我的实体超出其原始用途的范围,我还将打破“单一责任原则”。 为了代码可维护性,我应该保留原始实现,并使用其他方法对其进行扩展。 设计更好的实体 在采用开放式封闭原则的所有原因中,最重要的是它鼓励我们通过使用泛型和面向协议的编程(POP)原则来创建具有良好抽象平衡的实体。 试想一下,如果我知道我不能回到自己的实体来更改类型,那么我将非常谨慎地以能够提供最大灵活性的方式进行设计。 结果是我们最终可以编写更少的代码,并使我们编写的代码适用于更多情况。 简而言之,我们编码更好。 显然,这里可能会有更多的讨论,但是为了继续学习一些有用的技术,让我们考虑最后一个大问题…… 我应该严格遵循开放式原则吗? 遵守开放式封闭原则是一个主观选择,开发人员通常在何时扩展实体上有不同的看法。 毕竟,您的实体可能被认为是“完成”或“进行中的工作”,最终您作为开发者是做出这种区分的仲裁者。 以我的经验,如果您决定实体需要您想要实现的功能来履行其单一职责,那么请继续添加它们(无罪感!)。 否则,这里有一些有关如何正确扩展它们的想法。 扩展技术 在下一部分中,我们将介绍三种扩展技术并对其进行批判。 事不宜迟,让我们熟悉在以下示例中将要使用的惊人实体: 是的,这真是好人。 技术#1:协议扩展(OK) 假设我希望我的“ Thingy”能够摆动(不要问我为什么……)。 我的第一个直觉可能是这样做: 显然,这违反了我们的开放式封闭原则。 因此,一种方法可能是创建一个声明该方法的协议,然后将其实现放入我们的struct的扩展中: 很好。 请注意,方法的声明发生在协议中(第9行),其实现发生在实体的扩展中(第15行)。 我们可能考虑的一种变体是将方法的实现放入协议扩展中。 这将使我们能够在符合协议的任何实体上使用相同的方法实现。 看起来像这样: 这里要注意的最大区别是,我们的摆动方法的声明和实现现在都在协议的扩展中(第9-15行)。 当实体符合协议时(第17行),现在无需实现该方法。 因此,通过第一组示例,我们从技术上扩展了我们的实体,而没有对其进行修改,但是请注意! 首先,这种技术仅适用于方法,不适用于属性,因为我们无法在扩展中存储任何内容。 其次,也许还有一个更大(但不太明显)的缺点是,我们不再能够像添加摆动方法之前那样使用“ Thingy”的原始实现。 技术#2:类继承(OK) […]

SOLID Swift:第二部分

这是SOLID Swift系列的第二部分:在本文中,我们将研究Liskov替换和界面隔离 。 如果您想追随单一责任原则和开放/封闭原则 ,请随时阅读第一部分。 如简介中所述,第一部分是关于SOLID的前两个原则。 单一责任原则和开放/封闭原则已经是改进代码的两种重要方法! 接下来的两个: liskov替换和接口隔离 。 旁注:如第一部分所述,原则不仅是黑白的,还应根据您的情况,需求和要求进行调整。 非常清楚地表明违反了Liskov替代原则的危险信号之一是: if shape is Rectangle { … } else if shape is Square { … } 如果要添加新形状怎么办? 也许是Triangle ? 我们将不得不在列表中添加另一个 if。 另一个流行的例子更加微妙,可能不会马上被注意到。 想象一下具有width和height属性的Rectangle结构。 Square可以从Rectangle派生,因为正方形也具有宽度和高度。 但是,区别当然是对于正方形, width == height和矩形,该规则不适用。 如果我们要更新Rectangle.width ,那就好了,这对于Square.width因为Square.height尚未更改,因此违反了平方的规则。 同样,这违反了LSP。 这听起来像开/关原理吗? 没错,开放/封闭原则与LSP息息相关。 LSP走得更远。 LSP的基本规则是:派生类型(例如Square)必须完全可以替代其基本类型(在这种情况下为Rectangle)。 我们必须确保新的派生类型不会改变其行为的基础。 在开发过程中要记住的有关接口隔离的主要规则是:不应强迫客户端依赖于不使用的接口。 想象一下,我们有一个Employee ,它具有两个功能: lunch()和work()因为那是每个Employee所做的,对吧? 我们定义一个协议:可以通过func lunch()和func work() ,我们定义了class […]

使用IGCStatefulView管理UIView状态

当今大多数(如果不是全部)应用程序都会向其用户显示某种数据,无论是体育应用程序的记分板,新闻应用程序的各种新闻和文章,还是金融应用程序中与股市相关的信息。 这些应用程序希望一直向其用户提供有价值的信息,因此在某些情况下,这些应用程序将无法正常运行,例如网络错误或服务器错误。 然后,应用程序应该能够以某种方式向用户显示这些错误,使用户可以清楚地了解他们尝试查看应用程序中的信息时所发生的情况。 对于移动应用程序开发,表视图有一个特殊的子类别,它可以执行这种数据处理,即有状态表视图 。 这些表视图就其对应的数据集传达了不同的状态。 大多数有状态表视图都围绕以下三个状态: 加载,成功,错误 。 载入中 是当应用程序仍在处理其数据以将其作为数据源馈送到其表视图时。 成功 是指应用程序成功处理了数据后,在此过程中将其显示在表格视图中。 错误 是当应用程序遇到任何可能阻止应用程序获取必要数据的错误(上述示例)时。 现在,您可能想知道如何处理此类情况。 幸运的是,对于我们的iOS开发人员而言,Cocoapods拥有大量的图书馆,可以为我们完成工作。 考虑到这一点,让我们专注于一个库,它不仅显示表视图的状态,而且还显示集合视图和一般视图的状态-IGCStatefulView。 顾名思义 , IGCStatefulView的功能与其他状态表视图库相同,但是它的优点之一是,除了对UITableView实例的支持外,它还可以容纳UICollectionView和UIView 。 其背后的想法是数据的显示不仅限于UITableView实例。 由于它的功能类似于UITableView,因此也可以通过UICollectionView完成。 另外,并非所有数据都以列表形式显示。 例如,假设有一个新闻页面的详细信息页面。 虽然合理,但大多数时候,显示相关信息是通过UIScrollView或UIView完成的。 希望您会发现此库对您的项目有用。 如果您想了解有关IGCStatefulView的更多信息,以及如何将其集成到您自己的项目,使用它或为它做贡献,请检查其GitHub存储库。 干杯! 吉尔斯·兰伯特(Gilles Lambert)摄

Swift:没有人会打扰的常见错误-属性和函数调用的明确性

您好,我亲爱的开发人员, 我想提到的一件事是,Swift在函数和属性调用中引入了隐式性(可以避免调用self): 我们在ObjC遇到了这样的事情,但是到目前为止,这种疾病已经扩散到了各处。 使用该语言功能有几点要点: 没有语法高亮显示的代码不可读; 即使语法高亮显示,如果您不完全了解代码库,如果有全局函数和变量以及与全局变量同名的局部函数和变量,也很难得出所谓的东西。 当与属性一起使用时,开发人员往往会忘记,对属性的多次调用应缓存在局部变量中,因为在没有显式调用的情况下,他们只是不会将对属性的调用视为重复。 因此,我对所有人的建议是:不要使用快捷方式。 显式和重复数据删除代码,而不是在视觉上隐藏重复代码,而不在逻辑上隐藏它。 是的,像鸵鸟一样把头埋在沙子里,对您没有任何好处,因为属性不是万能的,无论您怎么想,它们可能或可能变得计算昂贵。 因此,为了排练,请明确: 就是这样,伙计们。 祝您有美好的一天,无论您身在何处,都要保持干燥。

iOS开发设置:自定义终端

iTerm + zsh + Prezto + Powerlevel9k 这篇文章是有关设置iOS开发环境的系列文章的一部分。 随着时间的流逝,我已经对终端进行了调整,以提供易于理解的大量相关信息,无论何时何地都可以。 归根结底,我有一个系统,可以在其中轻松判断我正在使用哪个版本控制系统,我在哪个分支中以及我的状态(未暂存的文件,已暂存但未提交的文件等)。 我可以使用zsh / Prezto内置的自动完成和建议功能快速导航到其他分支或文件。 iTerm允许我打开多个选项卡,即时切换配置文件,并轻松更新和调整终端的外观。 iTerm,zsh和Prezto的功能很多。 另外-看起来很好。 为什么zsh是您的朋友:http://www.slideshare.net/jaguardesignstudio/why-zsh-is-cooler-than-your-shell-16194692 关于Prezto为什么是Oh-My-Zsh的绝佳替代品的一些信息:http://joshsymonds.com/blog/2014/06/12/shell-awesomenessness-with-prezto/ iTerm可以为您做什么:http://lifehacker.com/5857046/the-best-terminal-emulator-for-mac-os-x 这篇文章将介绍我如何自定义终端的外观和功能。 有无数种组合,以下仅是我当前为iOS开发配置终端的方式。 iTerm2是MacOS默认终端应用程序的首选终端替代品,我已使用可与zsh主题Powerlevel9k完美搭配的配色方案和字体对其进行了自定义。 最后有指向我的自定义zsh点文件的链接。 iTerm 安装iTerm并将其设置为默认终端应用程序。 我强烈建议您阅读文档以了解所有可用内容。 iTerm主题: iTerm带有多个内置主题,以及一个简单的“导入/导出”功能,可提供更多功能。 其他主题可以在Google-fu上找到。 导入主题新主题: 预览,选择并下载新主题。 iTerm批准的主题可以在以下位置找到:https://github.com/mbadolato/iTerm2-Color-Schemes。 导航到iTerm首选项,导入下载的主题,然后选择。 您可以在帖子顶部的屏幕截图中看到经过稍微调整的Chalkboard主题版本。 如果愿意,可以调整标准或自定义主题以适合您的需求,然后导出以与他人共享。 注意:您将需要在iTerm中打开一个新的标签或窗口来查看您的更改。 这对于字体,颜色以及您对终端外观进行的任何其他更改均适用。 iTerm字体: 我已经沿着几种不同的路径设置了一种可以与iTerm和Powerlevel9k一起使用的字体。 Powerlevel9k建议的方法是使用Awesome终端字体(此处有详细的教程),这是一种优雅的方法,但有时也很精致。 在3个不同的MacOS更新破坏了我的字体设置之后,我选择了一个更简单的解决方案。 从NerdFonts下载要尝试的修补字体 下载的内容可能包含不需要的额外字体(看着您,Windows兼容字体)—继续并删除您不需要安装的字体 安装要使用的字体。 我喜欢Powerline Nerd Font Complete的DejaVu Sans Mono,您可以在帖子顶部的屏幕截图中看到它的使用。 如果您想更进一步,请在MacOS的Font Book应用程序中组织新字体,以便于浏览。 在iTerm中,将字体更改为首选的修补Powerline字体: 进出口: 按照自己喜欢的方式设置iTerm后,就可以导出配置文件并将其保存在安全的地方,以进行备份或导入到另一个实例。 […]

MVVM和VIPER之间的界线模糊

最初发表在 Swift Post上 。 如果您开发移动应用程序已有一段时间,那么您可能听说过MVVM和VIPER。 虽然有些人说MVVM不能很好地扩展,但另一些人则说VIPER太过分了。 我在这里解释为什么我认为它们非常相似,我们甚至不需要将两者分开。 首先让我们快速了解一下MVVM和VIPER。 View提供用户操作以查看模型 。 视图模型处理这些操作并更新其状态。 然后, 视图模型通过数据绑定或使用委派或块手动通知视图 。 视图将用户操作传递给演示者 。 然后, 演示者将这些动作传递给交互器或路由器 。 如果该动作需要计算,则交互器将其处理并将状态返回给presenter 。 演示者然后将此状态转换为演示数据并更新视图 。 导航逻辑封装在路由器中 , 演示者负责触发它。 有关这些架构的更多信息,请参考 Bohdan Orlov的 这篇精彩文章 : iOS Architecture Patterns 我们的首要目标是将UI和业务逻辑分开。 因此,我们可以轻松更新UI而不破坏任何业务规则,也可以隔离测试业务逻辑。 MVVM和VIPER都保证了这一点,但是方式不同。 如果从这个角度来看,它们的结构如下。 MVVM在UI层中只有一个视图组件,而VIPER将UI职责分为三个组件,即View,Presenter和Router。 同样很明显,业务层看起来几乎相同。 让我们看看它们在实践中在UI层中实际上有何不同。 想象一下,我们正在使用MVVM构建一个简单的应用程序,该应用程序将从IMDB中获取前25部电影并将其显示在列表中。 这些组件可能如下所示。 几周后,苹果发布了iOS 26,乔尼·艾夫(Jony Ive)通过引入全新的设计系统,拉开了一个新招。 我们的设计师兴奋不已,并迅速提出了适用于iOS的新UI。 现在,我们的工作将是实施此新设计并通过A / B测试证明其可用性,这意味着我们将仅向一定比例的用户显示新UI。 实际上,我们的信誉很好! 我们只需要为iOS创建一个符合MovieListView协议的新视图,并将其与演示者连接即可。 十分简单。 protocol MovieListView: […]

iOS-SOLID原则第2页-开放/封闭原则

打开/关闭原理指出模块应该打开以进行扩展,而关闭则可以进行修改。 从本质上讲,在iOS中,这意味着应该通过扩展打开模块,无论是通过快速扩展(或Obj-C类别),子类还是通过依赖项注入(或可以提供的其他任何扩展模块)。 使用扩展模块的任何其他模块仍应能够使用它。 这与SRP原理非常吻合,因为您无需使用该模块更改其他模块中的任何内容。 我将演示第一个示例—在第一部分的ImageStorage类示例中进行子类化。 假设我们要添加一个新的ImageStorage类,它所做的只是保存带有黑色效果的图像。 我们要做的就是子类化ImageStorage并覆盖它的saveImage函数: 如你看到的 我们仍然可以像以前一样使用ImageStorage 。 它没有任何改变,因为我们以对扩展开放的方式编写了ImageStorage 。 它应保持关闭状态以进行修改,因为任何修改都可能使其他模块以我们不希望或期望的方式运行。 对于此示例,我们将修改ImageStorage类的工作方式。 我们将为其添加文件访问器。 从现在开始,所有文件I / O将成为访问者的工作。 (这也将给我们带来更高级别的职责分离。SRP FTW!) 首先,由于我们使用依赖注入,因此ImageStorage现在不能使用静态函数。 接下来,我们需要声明文件访问器协议: 现在,我们需要更改ImageStorage类。 我们将通过init()注入FilesAccessor,从现在开始,加载和保存功能将与FilesAccessor一起使用: 看看SRP效果如何? 现在,ImageStorage不再关心文件访问的责任。 现在,我们可以实现我们的DocumentDirectoryFileAccessor: 最后,我们需要修改ProfilePicManager以使其与具有特定FilesAccessor的特定ImageStorage一起使用 。 例如,我们可以这样做(第9行): 现在,我们可以欣赏代码的灵活性 我们可以走这行: let imageStorage = ImageStorage(访问器:DocumentDirectoryFilesAccessor()) 并替换为: let imageStorage = NoirImageStorage(访问器:RemoteServerFilesAccessor()) 然后,我们会将带有Noir效果的图像存储在服务器上 ( RemoteServerFilesAccessor将与某些服务层模块一起使用)。 视图控制器对此更改一无所知,并且扩展我们的ImagesStorage现在是无限的。 我们可以声明所需的任何访问器,并将它们注入到任何ImageStorage中 ,只要它们符合FileAccessor即可 。 这导致我们进入第3部分: Liskov替代原理。