作为开发人员生活的愿望是在不同的开发领域中发展技能。 作为Android开发人员,我也有同样的看法。 我很想在iOS开发中发现自己。 到目前为止,我还没有遇到过一个机会。 我很想分享我对iOS的坚持。 基本上,Android具有四个主要组件,即Activity,Service,BroadcastReceiver和Content Provider。 活动 -它充当应用程序的入口点。 必须很简单,它只是一个带有背景的屏幕。 它有自己的生命周期。 服务 -它可以在后台执行长时间运行的操作,并且不提供用户界面。 广播接收器 —这是另一个组件,允许您注册系统或应用程序事件。 事件发生后,Android运行时会通知该事件的所有已注册接收者。 ContentProvider-仅当您需要在多个应用程序之间共享数据时,才需要内容提供者。 活动与ViewController 我们可以说Activity等同于ViewController ,因为它具有自己的生命周期(Android中为onCreate,iOS中为viewDidLoad),但并不完全。 它有点像 活动不能包含子活动。 在iOS中,一个UIViewController可以容纳另一个UIViewController 由Intent调用的活动,但在iOS中通过presentViewController或pushViewController调用 iOS中的服务 在Android上,只要授予正确的权限,您就可以执行所需的任何操作。暂停应用程序时可以使用服务来实现后台处理。基本上,您可以在iOS中执行操作,但是它受到严格的监管。 它将调用特定的方法来给您更新的机会,那么您的时间有限。 当应用返回后台时,您仍然可以要求更多时间(大约10分钟)。 查看beginBackgroundTaskWithExpirationHandler方法。 iOS中的广播接收器?? 担心说不。 因为iOS没有广播接收器。 在Swift中,为了达到相同的效果,可以使用NSNotificationCenter。 通过电池操作让这两个代码段都有。 创建了BatteryLowBroadcast接收器,并将该接收器注册到清单文件中。 在iOS上,应用无法以这种方式共享资源。 出于安全原因,每个应用程序都有自己的数据,并且由于能够访问另一个应用程序中的数据而被沙盒化。
本文是同时介绍TDD和多个CI概念的指南的一部分。 您可以在 此处 查看介绍性文章 。 在一个简单的项目(例如,有一个开发人员)下处理和管理代码通常是一件容易的事。 但是,当更多的人开始加入时,项目将增长,代码将获得更多功能,管理任务将逐步升级,并且很快就无法实现。 CI的基本原理是,通过不断审核更改,可以防止(或至少减少)此类集成问题。 每次提交后(如果您使用的是git),CI都会构建您的代码并运行测试套件,以确保最新的更改不会破坏任何内容! CI还可以在单独的计算机上(无论是否在本地)运行,以确保代码在与用户的计算机不同的计算机上运行,并且该代码也可以在“干净”的计算机上构建,而无需事先安装任何吊舱等。 在为我的Swift项目寻找合适的CI时,我遍历了其中三个: Travis CI:基于云,它提供了一个VM,供您在其中运行代码。适用于XCode项目,您可以运行xcodebuild xctool甚至fastlane命令。 简单的界面,但至少到目前为止,它仅与GitHub项目配对。 它是免费的开源项目,并且为私有存储库提供了不同的层次。 Circle CI:非常类似于TravisCI,也是基于云的。 它的配置有些不同,但没什么特别的。 它的免费套餐仅适用于Linux机器,并已付费MacOS平台(提供免费的两周计划) Jenkins CI:与前两个相反,Jenkins是免费的,但您必须将其托管在自己的服务器中。 它具有更多的可定制性,但这是以不那么友好的配置为代价的。 它的学习曲线更陡峭,但是如果您想对事情进行更多的控制,这可能是我的选择。 此外,我还想包括其他工具,这些工具将有助于检查测试覆盖率,代码质量和棉绒: CodeCov:有关测试覆盖率的报告 SwiftLint:分析代码中可能的错误,样式错误和/或可疑的内容; 猎犬:类似于SwiftLint,但基于云 Codebeat:通才代码审查,检查代码重复,长函数或语句等 让我们从这里开始看看该工具如何与GitHub存储库一起使用。
令人恐惧的XIBs与Code辩论一直使我感到烦恼,这是因为两者无法并肩工作,同时又无法为主题和调色板保留一个真理来源。 出于这个原因,我主要是在参数的代码方面发现问题,经常在查看带有XIB / Storyboards的100多个UI类的项目时,会发现等待着大多数中型主题的启示性迹象XCode项目。 不仅每个XIB都是使用其自己的筒仓字体和颜色集创建的,而且甚至不一致。 有些人盯着眼睛,有些人只是放弃了,有些人却久违了AB测试的鬼魂……而任何人都被要求在应用程序中的每个按钮上绕一组角或更改特定颜色,因为它不太正确品牌的绿色阴影再也不会使人感到恐惧。 如果没有适当的计划和沟通,维护XIB就是一场噩梦。 有一个答案(至少对于UIColor而言 ),它在.xcassets内的ColorSets似乎未得到充分利用…… Assets.xcassets>快速设置新颜色 产生一个相对简单且可维护的颜色库。 我个人的喜好是将其更改为8Bit Hexadecimal,以说出与大多数图形设计师相同的语言。 在那里…去任何XIB或Storyboard,您都会找到定义的颜色… 在代码中, 只需添加带有警告的UIColor(named:“ background”)就很简单,可惜这些都是可选的。 为了避免在每一个转弯处添加问号并防止将来使用魔术弦,我创建了一个小的辅助扩展程序… 现在只需要让bgColor:UIColor = .colorSet(.background)即可 。 也许这并不是解决我们困境的完整答案,但即使是Code vs XIB阵营中任一派中最坚定的支持者,也可能会达成共识。 它是可维护的,易读的,而且最重要的是,它是调色板的唯一事实来源。 ●●●●
学习iOS和Swift开发 我的一个朋友建议我参加iTunes U上的“使用Swift开发iOS 9应用程序”课程。这是斯坦福大学免费提供的一流课程。 讲师Paul Hegarty是一个绝对的传奇人物! 强烈推荐。 软件开发很难 该死的! 我喜欢Apple的愿景是下一代学习与数学和英语一起编码的知识。 但是请不要误会,这不是孩子的游戏。 为了使事情变得更困难,我在课程中途更新了macOS Sierra。 由于新版本的Xcode仅适用于Swift 3,这使我所有的学习资料都已过时。该学习资料是用Swift 2编写的。Facepalm! 最后一切都很好。 我设法将代码手动更新为Swift 3,但我不推荐这样做。 我在过去的7年中一直担任设计师,但是我也用HTML,CSS,JavaScript,SASS,jQuery进行编码,并编写了有关面向对象开发的教程。 但这仅帮助我在使用Swift学习面向对象开发的过程中获得了约20%的帮助。 而且,我只刮了一下表面。 话虽如此,完全有可能自己学习iOS开发。 无需为训练营支付超过10,000美元。 怪胎说话 现在,我可以与真正的iOS开发人员进行半清晰的对话,讨论闭包,可选控件,委托,协议,Xcode,结构,枚举,CocoaTouch,UIKit,情节提要,自动布局,模型视图控制器,segues,表视图控制器,核心数据,云工具包,应用程序生命周期。 使用Swift学习iOS开发的提示 您需要了解面向对象编程背后的基本理论。 大量的在线学习资源。 您需要至少一个月的全日制学习,才能在iTunes U上使用Swift开发iOS 9应用程序课程。尽量不要像我一样在中途更新Xcode。 大型显示器会有所帮助,但您可以在笔记本电脑上完成所有操作。 Stackoverflow.com是您的新好朋友。 您的其他新朋友是raywenderlich.com,reddit.com / r / swift,reddit.com / r / iOSProgramming,并确保查看ios-developers.io Slack社区。 构建简单但至少在您眼中真棒的东西。 查找教程来填补您的知识空白。 然后,在App Store上获取您的应用。 下一步是什么? 我正在构建我的第一个iOS应用。 Minihero是一款旨在帮助人们互相帮助的应用程序。
应用程序体系结构是移动开发中的热门话题,并且有一个原因–每个应用程序都需要某种逻辑形式的结构化代码以保持可靠,可扩展和可维护。 iOS应用程序没有什么不同。 最受欢迎的体系结构之一是Model-View-ViewModel,其中视图控制器和视图属于“视图”部分,而视图模型是负责应用程序业务逻辑的单元。 我本人一直在使用此体系结构,还曾见过其他人的多种实现,但是它们似乎都不令人满意,它们显然错过了一些东西。 这些根组件都不适合执行导航或创建控制器。 在控制器负责这些任务的情况下以MVVM方式执行操作感觉很错误,但是我们必须这样做吗? 在MVVM领域中,用于处理路由的一种可能的解决方案是,视图模型公开一个接口,该接口告诉视图控制器何时何地应该路由到何处。 但是,此解决方案远非理想–它使视图控制器意识到其在应用程序中的位置,从而降低了我们以后重用它的能力。 解决此问题的更好方法是引入一个附加组件,这是经典MVVM所缺少的。 有两个常用的对象–路由器和协调器。 两者都是有效的解决方案,它们使单元测试的某些部分非常简单,但是,有一个关键的区别–路由器从视图控制器的单个实例管理路由,而协调器则负责整个流程。 哪一个更好? 与往常一样-没有适合所有应用程序的灵丹妙药解决方案。 如果您的应用程序有很多独立的屏幕,可以在不同的上下文中显示-您可能应该使用路由器,如果它的屏幕可以分为几个控制器-长流程,则协调器可能是更好的解决方案。 我现在正在处理的应用程序属于第一类,因此我一直在使用路由器。 让我解释一下为什么它们比没有它们的路由有这么大的改进。 请记住,以下大多数属性都是与协调员共享的。 路由器的界面不需要了解UIKit,它所使用的控制器很可能只是暴露了push , present和dismiss等基本方法的协议,因此,路由器易于测试,并且可以在不考虑平台的情况下使用或设备。 路由器的导航界面是唯一的界面。 如果使用View Controller执行路由,则可能要处理甚至可能不感兴趣的大量方法–同时,路由器的接口仍然很小,非常简单并且可以完全测试。 尽管您不一定需要测试路由器,但是这种简单性可以使视图模型测试更加简洁,因为对路由器的调用通常是复杂的视图模型逻辑的结果。 为了充分利用路由器,我们需要将它们注入到我们的视图模型中。 我们通过为每个路由器使用协议来实现这一点,并且可以解锁更多令人惊奇的特性: 几乎在每个屏幕上都会执行一些与UI相关的常见操作,例如活动指示器的显示和错误/成功消息的显示-这些可以放置在所有路由器的某些根协议中,例如RouterType,因此我们可以避免很多操作不必要的代码重复。 通用路由器功能可以使用默认实现封装在协议中并组成。 例如,我们可能希望我们的路由器能够向SafariController提供一些URL –这不是所有路由器都需要做的,但是我们可能在一些地方使用它。 我们要做的就是创建一个具有默认实现的协议,并且可以与其他协议(例如ImagePickerRoutable或DocumentBrowserRoutable)进一步组合。 路由器的使用还有一个很大的属性-它使提取与导航相关的通用逻辑变得非常容易。 假设您有很多警报,操作表或弹出窗口,需要用户执行某些操作,然后执行一些任务并关闭。 如果随后应执行某些UI动作(例如活动指示器或其他控制器的表示),则视图模型通常会将其通知给视图控制器。 现在,如果需要在多个应用程序中处理相同的动作,我们可以轻松地将逻辑提取到单个可重用的单元中。 但是如何处理那些与导航相关的动作呢? 如果由控制器处理它们,那么所有的人都需要这样做-这可能是大量的代码重复,而且浪费时间。 对于路由器,这个问题根本不存在-我们可以将其与一些常见的处理程序一起重用。 我还要在这里提一些与视图模型相关的实践: 视图模型不应该是数据源,而应该公开单元配置所需的数据-这对于测试复杂的tableViews和collectionViews特别有用。 您还应该将数据源创建为单独的对象-将来可以轻松重用它们,并且立即进行操作不会有任何危害。 用于填充特定视图(例如UserTableViewCell)的数据应包装为单个结构,例如UserCellConfiguration。 这种结构只是实际数据与其转换之间的薄薄一层,用于填充所有标签,imageViews等。 它使实际模型和视图之间的区别更加容易。 使用依赖注入–可以在单元测试中使用模拟对象,使其易于编写。 视图模型不应导入UIKit-这不是至关重要的事情,但是如果您牢记这一点,它将有助于您保持UI与逻辑层之间的分离。 除了介绍路由器之外,我还在应用程序中使用了另一个组件– ControllersBuilder。 ControllersBuilder只是一个简单的结构,能够在整个应用程序中创建所有控制器。 每个构建方法都遵循相同的方案: 初始化路由器并自行注入,因此此类路由器可以请求创建另一个控制器 初始化视图模型,注入所需的依赖关系,初始数据和路由器 初始化视图控制器并注入视图模型 将视图控制器分配给路由器的弱属性 这里要注意的重要事情–最好让构建器方法返回普通的UIViewControllers,而不是返回特定子类的实例。 […]
今天我在谈论Swift中的self关键字。 让我们先检查一些代码。 这是关于属性初始化的错误使用,但是开始我们的主题是个好方法。 这是运行后的日志: 有人可能会问为什么self在这里是一个函数,它不应该是当前类的实例吗? 好,让我们添加一些东西。 和添加lazy关键字后的日志: 看到, self成为我们现在期望的当前类的实例。 那么, lazy这个魔术词做什么呢? 也就是说,关键字lazy确保默认闭包内的self被初始化并引用当前实例。 但是,当没有lazy声明时, self指的是什么? 我们知道许多语言都有关键字self ,例如Ruby和Python。 还有许多其他语言具有相同的模式,但是在JavaScript,C#和Java中使用了不同的关键字,例如this 。 而且,JavaScript中的this始终是指诸如global东西,但是迅捷的self并不总是具有意义。 让我们进一步了解我们在一开始所声明的内容。 看来我们可以得出结论了。 开始时的self是指NSObjectProtocol中的self 。 让我们做一个小实验: 我们声明一个名为SomethingNotInheritedFromNSObject的类,该类未从NSObject继承。 并且self在初始关闭时变得不确定。 这就是Swift中的自我。
每个开发人员都希望在创建产品时尽最大努力。 通常,它需要超越自然设计的新颖感。 幸运的是,应用程序扩展允许开发人员在常规应用程序的边界之外添加自定义功能。 此扩展名的类型很少,其中一种是自定义键盘。 从iOS 8开始,开发人员可以使用此功能扩展其移动应用程序。 系统键盘很棒,很舒适,而且功能齐全,但是在某些情况下,第三方键盘可以通过滑动或预测用户意图等功能来加快文本输入速度(这是一种什么样的法术?)。 引入它时,我的第一个想法是创建一个简单的密码管理器。 在小型非物理键盘上输入复杂的密码从未如此简单。 从理论上讲,我只需要构建自己的自定义键盘并从此过上幸福的生活。 但是,后来我了解到某些并发症,例如… 自定义键盘限制 这种局限性可以用一句话来概括:不允许自定义键盘访问标准应用程序通常可以使用的许多功能。 首先,关于我的密码管理器,Apple担心用户隐私。 这就是为什么我们在与安全字段进行交互时不能使用自定义键盘的原因,因此无法创建带有魔术按钮的键盘来为我们完成所有这些密码键入操作。 此外,您不能在普通键盘框架上绘制任何元素。 真可惜,但是它与Apple的人机界面设计规则很好地结合在一起,就是这样。 除此之外:我们无法通过“自定义键盘”访问麦克风和摄像头,这使得听写输入变为不可能。 正如您所期望的那样,该列表还在不断增加。 您可以在《人机界面指南》中阅读有关限制和良好实践的更多信息 ……和必备 必须提供一个按钮以切换到其他键盘或返回普通键盘。 在系统键盘中,是那个神秘的地球仪图标按钮。 为了不干扰用户的期望,习惯和根深蒂固的行为,我建议保持这种状态。 我们需要做的就是将带有所有事件的addTarget添加到我们的键盘UIInputViewController到方法advanceToNextInputMode()中(用户可以点击此按钮以快速切换到下一个按钮,或者点击更长的时间以查看可用键盘的列表)。 要使用Internet连接,用户必须允许完全访问自定义键盘。 它允许键盘与包含的应用程序共享数据。 因此,该应用程序可能成为键盘的管理器应用程序。 因此,它可用于同步用户词典或调整并保留用户首选项。 但是,键盘必须在没有完全访问权限的情况下始终可用,因此也需要没有互联网连接。 实施技巧 只是一些代码片段 我不想逐步介绍您,因为我确定您知道如何创建xcode项目,添加视图等。要开始,您需要添加一个新目标(文件->新建->目标->自定义键盘扩展名)到现有项目。 您将获得InputViewController子类,它是键盘的起点-您可以在此处添加控件。 让我们跳到很酷的东西。 例子在Obj-c中,但是基本上它们没有任何特定于语言的怪癖,如果您使用Swift编写,翻译它们会非常容易。 我还建议阅读Krzysztof Pelczar文章中的更多实施技巧。 处理回车键类型和键盘类型 如果要创建可用作主键盘的应用程序,则需要准备不同的文本输入方案。 为了方便用户,您可以在用户输入电子邮件地址时添加“ @”按钮而不是其他按钮,或者在编辑仅允许数字的字段时仅显示数字键盘。 您可以通过在InputViewController中调用来获取当前的键盘上下文(UIKeyboardType): 我不建议使用可视界面构建器来放置按钮,因为完成该过程将花费更长的时间,并且从长远来看将变得很难维护。 通常,在我们的项目中,我们使用具有布局约束(可视格式语言-FTW!)的代码创建视图。 但是这次我决定计算每个键盘按钮的帧将是更好的解决方案。 它只会被计算一次,不需要在相邻视图之间保持约束,并且可以非常快速地调整大小。 一段代码只是为了展示它是如何完成的: 处理长按按键重复 当用户点击删除按钮时,他希望它将删除文本,直到他抬起手指。 这就是它在本机键盘上的工作方式,这也是根深蒂固的用户行为之一。 但是有一个缺陷。 据我所知,没有手势识别器会在每个给定的时间间隔被调用。 但是我们可以使用UILongPressGestureRecognizer和NSTimer做一个简单的技巧。 结果,当用户将手指放在删除按钮上时,我们每150毫秒接收一次事件。 […]
“ 通用编程是一种计算机编程风格,其中,算法根据稍后要指定的类型编写,然后在需要以特定类型作为参数提供时将其实例化 。” — Wikipedia 使用泛型编写代码是一种编写函数和数据类型而无需指定需要使用哪种确切类型的方法。 顾名思义,泛型类型不是特定的。 通过使用泛型,我们可以编写非特定的代码,因此,我们可以以更清洁,错误更少的复杂方式抽象代码。 在第一部分的这篇文章中,我将讨论数组,字典和可选变量是泛型的示例。 第二部分将讨论编写通用数据类和结构,编写通用函数和约束通用类型。 第三部分将讨论“关联类型”和“通用where子句”。 泛型数组 数组是泛型类型的示例。 数组是容纳任何类型的事物的容器。 在Swift中,我们受益于类型推论,我们可以在不显式声明其类型的情况下创建数组。 由于我们不必指定任何数组的类型(因为可以推断出该数组的类型),因此该数组作为数据结构是通用的。 让月份= [“一月”,“二月”,“三月”] 上面,我有几个月的时间。 由于我给的数组类型参数是字符串,因此本月的实例被赋予了STRING的具体类型 。 换句话说,声明数组时应定义的类型。 数组的通用语法 尽管上述月份的数组未正式将类型声明为STRING,但是有一种通用的语法声明数组,如下所示: var springMonths:Array = [] 请注意,单词Array后跟尖括号中的类型。 我们可以在这些尖括号之间分配“通用类型”的名称。 对于泛型,规范是使用作为我们引用的任何泛型类型的占位符。 在数组上使用通用方法 数组是通用的,带有通用方法,例如.append() springMonths.append(“ March”) springMonths.append(“ April”) springMonths.append(“五月”) 您会在上面注意到,我在springMonths数组中附加了月份“ March”,“ April”和“ June”。 如果我接下来编写以下代码行,您会怎么办: springMonths.append(50) 如果您猜编译器会给我错误: 那你是对的! 由于我们声明了Array的类型为STRING,因此任何不是字符串的东西都会使编译器不满意。 泛型词典 字典也是泛型的,因为我不必直接指定每个键的类型或每个值的类型。 例如,在下面,我没有具体说明字典类型: let monthDaysDictionary = [“一月”:31,“二月”:28,“三月”,31,“四月”:30,“五月”:31,“六月”:30,“七月”:31,“八月”: 31,“ […]
该博客旨在逐步详细地介绍如何在iOS应用上设置Google Maps。 对于此博客,任务是在The Flatiron School放置一个Google Maps标记,以便每个人都知道我在哪里学习。 第1步-安装Cocoapods Google Maps SDK可作为Cocoapods窗格使用。 当您听到术语Cocoapods pod和SDK时,有些人可能会像这样this。 不用担心,那很好。 不久之前,我的脸也一样。 让我们首先来看一个简单的SDK。 SDK是指软件开发人员套件。 Cocoapods是管理Xcode项目库依赖关系的工具。 换句话说,Cocoapods是使我们的生活更容易开发软件的工具。 如果尚未安装Cocoapods,则只需转到终端(按Command + Space并编写Terminal)。 在那里,输入: 须藤宝石安装cocoapods 等待安装完成,然后输入“ pod”。 如果看到此图片,恭喜! 您已经安装了Cocoapods。 如果您对此还有其他疑问,请访问https://cocoapods.org/ 第2步-创建Podfile 首先在您的Xcode中创建一个项目。 为了撰写本博客,我们将创建一个名为“ Google Maps”的单一视图项目。 然后,打开终端并转到包含您的项目的目录。 在那里,输入“ pod init”。 这将创建一个名为“ Podfile”的pod文件。 下一步是编辑该文件以添加Google Maps依赖项。 我们将使用文本编辑器打开文件来完成此操作。 我使用Atom(https://atom.io/)。 如果您不这样做,我强烈建议您开始使用它。 如果您有Atom,只需在终端中编写“ atom Podfile”,pod文件将在Atom中为我们的版本打开。 在文件中添加“ pod’GoogleMaps’”并保存(Ctrl + S)。 您的文件应如下所示: 最后,我们准备在项目中安装Podfile。 为此,我们只需键入“ pod […]
快速地,类似的赋值语句具有两种不同的行为,在一个地方它的行为就像类型推断,而在另一个地方它的行为就像类型赋值。 swift中类型推断和类型赋值之间存在细微差别。在类型推断中,它直接复制变量的类型,就像在let语句中将赋值的赋值中那样,这意味着将其解包为可选 输入推断示例: 现在名称的类型为Optional 意味着名称完全推断了str变量的类型,该变量为Optional String 类型分配示例 结论 :两者都是赋值语句,但是第一个(name = str)是类型为infer的简单赋值,第二个(如果让plicateName = str {})是类型赋值。