Tag: uikit

使用单元测试测试您的UI

概括地说,我们编写的测试分为3个不同的类别: 单元测试是人们可以编写的最简单的测试,并且顾名思义,它可以独立测试单个代码单元。 它们既快速又稳定,我们可以编写很多它们,它们是其他测试的基础。 一个普遍的误解是,单元测试仅针对模型(即处理数据的实体)编写。 另一方面, UI测试是对整个事物进行整体测试的工具-它们从用户角度检查系统,并且极易损坏。 UI测试既昂贵又缓慢,并且是需要不断进行维护以跟上不断发展的系统的测试。 我们应该始终努力减少运行测试所需的时间-通过将大量的UI测试转移到单元测试,我们可以获得绝缘和速度方面的好处。 这是一个例子…… 假设我们在UIViewController中有一个UITableView,它应该覆盖整个视图。 我们的测试不应检查tableView是否正确“拉伸”(最终结果),而应检查视图是否具有上述约束(并相信系统在内部做正确的事情): 这个简单的测试比任何UI测试都快得多,并且不依赖于应用程序的其他部分来加载和显示View Controller(根据UI测试的要求)。 希望这可以帮助。 测试愉快! 为了完整起见,这是整个TasksViewControllerTests类:

Swift:基于UIStackView的网格布局

我们的目标是获得具有可变单元格数量的可重用网格组件。 让我们为网格准备一个容器。 请注意,此实现不支持滚动,要启用垂直滚动,您将需要使用UIScrollView包装网格。 为了让我们的网格动态调整大小,我们需要跟踪其中的当前单元格数量。 我们还需要知道一行可以包含多少个单元(一行的大小)以及网格单元的高度。 每当当前行超过其大小时,我们都将开始一个新行。 让我们看一下prepareRow()函数。 网格中的每一行都由一个水平的堆栈视图表示,并且由于我们希望所有像元具有相同的宽度,因此分配模式设置为fillEqually 。 为了向网格中添加新的单元格,我们将使用一个实例函数(我们也可以将一个单元格数组传递给GridComponent ”函数,这将是一种更美观的方法,但是在此实现中将省略它)。 我们的GridComponent几乎准备就绪。 但是我们仍然需要解决一个小问题。 我们在这里使用的水平堆栈视图具有fillEqually分布模式,并且如果最后一行中的单元格数量小于行的大小,则最后一行中的单元格将以不同的方式分布。 一种解决方案是使用伪造的单元格填充最后一行。 我们可以使用一个简单的UIView ,但是如果需要的话,我将创建FakeCell类来区分假单元格和网格中的实际单元格。 addCell函数的最终实现将如下所示: 现在我们完成了。 但是对于具有大量单元格的网格,我仍然建议使用UICollectionView来提高性能。 感谢您的阅读,如果您发现这篇文章对您有帮助,请别忘了鼓掌!

使UI适应iPhone X时从设备模型中抽象出来

仅使用“自动布局”使按钮具有自定义外观 我们的设计师最近要求在屏幕底部制作一个如下所示的按钮: 在这篇文章中,我将展示如何在不依赖设备模型查找的情况下实现这种自适应UI。 心态 每年,当新设备问世时,人们一直在问如何以编程方式检测模型的问题(2017年也不例外)。 尽管可以这样做,但该API有点奇怪。 这是因为Apple努力教会我们不要这样做。 取而代之的是,他们要求开发人员在进行功能测试时依赖可用性测试,并依靠自动布局来制作自定义界面。 换句话说,抽象远离特定的设备模型。 好吧,在一个可能可行的理想世界中……从这个角度看事物有时确实会有所帮助。 安全区 我要做的第一件事是更改按钮的约束,以遵守主视图控制器视图的安全区域。 这立即解决了我们在iPhone X Simulator和Xcode 9中首次启动该应用程序时遇到的问题。 NSLayoutConstraint * buttonBottomConstraint = nil; 如果(@available(iOS 11,*)){ buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor]; } 其他{ buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.bottomLayoutGuide.topAnchor]; } 注意:如果您想知道bottomLayoutGuide.topAnchor ,这是因为bottomLayoutGuide具有id类型。 没有帮助吗? 好吧,这超出了这个故事的范围。 对不起that 然后,我决定重做按钮的所有约束,以坚持安全区域: NSLayoutConstraint * buttonBottomConstraint = nil; NSLayoutConstraint * buttonLeadingConstraint = nil; NSLayoutConstraint * buttonTrailingConstraint = nil; 如果(@available(iOS 11,*)){ […]

使用UIKit的连续圆角

在Fueled,我们花了几个月的时间来开发一款名为Toppler的超棒增强现实游戏,以探索ARKit 2的某些功能。 我们已经介绍了我们在前两篇文章中介绍的一些技术课程,其中大部分与SceneKit有关。 现在是时候回到UIKit并更深入地研究一些设计概念及其实现方法,以实现更为熟悉的框架。 我们将专门研究圆角。 不是通常使用layer.cornerRadius构建的layer.cornerRadius ,而是连续的圆角。 为使我们清楚区别,以下是Apple于2013年发布的iOS7中经典圆角和连续圆角之间的比较: 请注意,您可以在橙色视图后面看到非常细的红色。 这是底角和平滑角之间的实际差异。 对于使用Sketch的用户来说,设计连续角的属性称为“ 平滑角” : 苹果使用专用API来增强其在SpringBoard中的圆角。 这是var continuousCorners: Bool CALayer var continuousCorners: Bool 。 但是,如果您在提交给AppStore的应用程序中使用此API,它将被拒绝。 😕 互联网上有许多资源可以通过批准的方式达到相同的效果,通常涉及UIView子类及其自己的drawRect实现。 但是,我们一直希望有一个更简单的解决方案,您可以借助遮罩将其应用于任何UIView 。 事实证明, UIBezierPath(roundedRect:cornerRadius)产生一个连续角。 只需使用其路径是使用先前功能构建的CAShapeLayer完成的: Toppler中的增强现实代表了核心体验。 但是,我们一定不能忘记将所有其他一切联系在一起以创建华丽而独特的用户体验的其他视图。 而且,我的朋友们总是以无处不在的UIKit开始和结束。

如何创建自定义分段控件-Swift

通常,我们的设计器会创建一些不是UIKit模式的组件。 大多数时候,我们可以做出一些改变,但有时却不能改变。 今天,我将教您如何创建自定义分段控件。 首先,让我们来一个UIKit分段控件。 在大多数情况下,我们都有边框,色调颜色和区域,我们可以自定义此分段控件。 假设您要创建此组件。 您在界面构建器中没有底视图来修改或删除所选节的背景。 让我们开始吧! 注意:我不会使用IBInspectable / IBDesignable,但是如果您知道如何使用它们,则可以。 我们的CustomSegmentedControl是UIView的子类,而不是UISegmentedControl 首先,我们需要创建变量: 我们有三个私有变量,为什么要私有? 因为我们不希望其他类可以在没有updateView的情况下更改此值。 使用默认值来自定义视图的三个变量。 现在我们需要显示按钮,但是我们有一个数组,这个数组可以是无限的。 最好的方法是创建一个水平堆栈视图 。 堆栈视图使子视图具有相等的间距,而不使用任何约束。 您如何意识到我们创建了四个约束来使我们的堆栈视图充满所有自定义视图。 现在,让我们创建选定的视图。 我们希望所选视图具有相同的按钮宽度。 我们需要用相等的部分数划分视图框架。 让我们在数组中添加一些按钮,我们该怎么做? 使用我们的buttonTitles数组。 首先,我们删除按钮中的任何元素,并删除视图中的所有子视图。 我们可以从按钮标题创建一个for,并从我们的标题创建一个单独的按钮,让我们设置标题,颜色和动作也添加到按钮数组中。 在本教程中,我考虑始终选择零部分。 让我们实现这种情况,将颜色设置为索引零的按钮。 在我们的动作函子中,我们必须创建一个用于从数组按钮中枚举的for,因为我们可以访问单个按钮和数组中的索引。 首先,我们需要将按钮设置标题颜色设置为默认颜色,之后我们需要进行比较以找到单击了哪个按钮。 我们需要找到X位置,并使用UIView.animated将我们的selectorView移到该位置,然后将按钮标题颜色设置为selectedColor 现在,我们创建一个名为updateView的功能,以调用其他三个配置功能。 很简单。 我们可以创建一个便利初始化或/和创建一个传递数组的函子。 我要创建这两个。 为什么? 因为与您一起使用的是“查看代码”,我们将使用我们的init,但是如果我们要创建IBOutlet,则需要调用函数。 如果您运行代码,它应该可以工作。 记住在viewDidLoad()中调用CustomSegmentedControl

iOS开发课程:导航控制器

如何在单个导航堆栈中使用某些屏幕构建应用程序? 如何管理导航项? 让我们在快速指南中回答这些问题! 导航控制器是一种容器视图控制器,它在导航界面中管理一个或多个子视图控制器。 在这种类型的界面中,一次只能看到一个子视图控制器。 在视图控制器中选择一个项目会使用动画在屏幕上推送一个新的视图控制器,从而隐藏先前的视图控制器。 在界面顶部的导航栏中点按后退按钮可删除顶视图控制器,从而在下面显示视图控制器。 阅读有关Apple Developer的更多信息 创建一个新的Single View App项目。 热键:shift + Command + N 打开主板 如何将导航控制器添加到情节提要中? 只需按: Shift + Command + L 选择导航控制器并将其拖动到情节提要。 凉。 您的第一个导航控制器已在现场。 查看throw Attributes Inspector(在右侧)。 这是导航控制器属性。 *将导航控制器设置为初始视图控制器 (该控制器在程序启动时加载) 让我们向显示第二个控制器的第一个控制器添加按钮。 只需按: Shift + Command + L 选择“条形按钮项”并将其拖动到“视图控制器导航栏”。 设置标题。 打开连接检查器。 将动作从“触发的Segues”部分拖动到第二个View Controller,然后选择“显示”选项。 在模拟器上运行项目。 想要制作大标题功能吗? 只需在导航控制器中选择导航栏。 打开属性检查器 安装程序优先选择大标题 *对于将UITableView作为视图的View Controller(或视图的第一个子视图) 大导航标题将在滚动时自动过渡。 在模拟器上运行项目。 […]

TableViewCell管理のグッドプラクティス

Table,TableViewのCellはどのように管理していますか? 愚直に実装して,后から追加の要件が降って来たときに,DataSourceが目も当てられない状态になっていませんか? ではの记事では,TableViewのCellを良い感じに取り回すためのグッドプラクティスについてサンプルを交えて绍介したいと思います。 叩きとなるサンプルとして,TODOアプリを作成することを考えましょう。 要件 一番上にプロフィールのセクションを表示 カテゴリごとのTODOタスクを并べる 各カテゴリにはヘッダーを设ける この要件のTODOアプリを, 単纯に実装 中间表现を作成 抽象化 という3Stepで见ていきたいと思います。 単纯に実装していくと,以下のようになるかと思います。 表示するタスクの配列をプロパティとして持つDataSourceを作る 科の数 部ごとの行の数 科のヘッダータイトル 各行に表示する单元格 こんなにシンプルな要件なのにもう70行ですね… 辛いこと DataSourceのそれぞれのメソッドで开关文(或其他文)が乱立してしまい,DRYじゃない 查看のレイアウトが変わった际の影响范囲が大きい そこで,表示されるデータと表示するTableViewの间の中间表现として, 各节,行でTableViewに何を表示するかを表现する 二重配列 (外侧がsection,构成がrowを表す入れ子配列)を定义することで,上记の辛いことを解消していきたいと思います。 表示するタスクを二重配列として持つDataSourceを作る これにより, numberOfSections numberOfRowsInSection titleForHeaderInSection cellForRowAt で毎回indexPathによる分岐を作る必要がなくなりました。 科の数 部ごとの行の数 科のヘッダータイトル 各行に表示するCell 分岐の处理が共通化されて70行→ 55行とだいぶスッキリしましたね。 TableViewの状态を表す中间表现を二重配列として切り出すことができました。 Data,他の全てのDataSourceにおいて同じような处理を何度も书いていくのも面倒なので,これらを抽象化した衬底クラスStructuredTableCellDataSourceを作ります。 Table,TableViewの剖面,行を表す构造体を定义します。 TableCellSection.swift TableCellRow.swift そして,二重配列として表现していたTableViewの状态を, TableCellSectionの配列として持つStructuredTableCellDataSource を定义します。 表示するタスクをTableCellSectionの配列として持つStructuredTableCellDataSourceを作る UITableViewDataSourceの处理を共通化していきます。 (このあたりは先ほどとやっていることはほとんど同じですね) 科の数 部ごとの行の数 科のヘッダータイトル Cell,Cellの构成处理はまるっと抽象化できないので,次のように少し工夫してあげる必要があります。 […]

功能视图构建

在情节提要中还是在代码中创建视图? 作为iOS开发人员,我们非常清楚这个问题。 他们两个都有优点和缺点,但是最近我越来越喜欢用代码创建的视图。 什么是架构? 我曾经将MVVM与RxSwift一起使用,这基本上意味着控制器是通过结合Storyboard , ViewController和ViewModel来创建的。 如果我们删除Storyboard界面,则在哪里构建视图的正确位置? 让我介绍一下ViewBuilders。 考虑一下当我们构建一个称为HomeViewController控制器时的情况。 让我们创建一个名为HomeViewBuilder的帮助程序结构,该结构最终返回HomeView ,该抽象使我们能够访问组件(等效于插座)。 因此,我们最终得到以下架构: HomeViewBuilder的结构: 也许您已经猜到我们要通过管道化一些操作(例如添加/设置适当的组件)来构建此视图。 让我们定义一个简单的ViewBuilder协议: 我们还定义实现ViewBuilder HomeViewBuilder 。 当然,最重要的部分是buildView函数。 我故意向您展示了此方法的最终版本 。 这是唯一可以从外部访问的公共方法,这是一种梦想,我们现在要实现。 首先 ,让我们介绍流行的管道和函数组合运算符,这些运算符允许我们组合函数和对象: 管道运算符用于buildView函数中,其基本外观如下: 组成从类型Builder功能到Builder 将此转换应用于创建的构建器 好的,看起来不错,但是这些 install / setup 功能 如何 工作? 如果您对镜头了解不多,则一定要观看一些视频,了解这种模式的工作原理。 我在这里使用它们来编写单独的小段代码,并使它们可重用。 让我们考虑使用vertical轴创建UIStackView并将translatesAutoresizingMaskIntoConstraints标志设置为false 。 我们将在项目中使用多少个? 当然很多 我创建了一些项目全局的Style结构,该结构定义了常用的样式,这些样式是(View) -> View类型的转换函数。 这里有些例子: 因此,让我们构建使用其中一种样式的UIStackView 。 这是典型installer功能的实现: 多亏了镜头组成,它看起来很清晰,我们在应用程序中获得了很多可重用性。 但是,等等,我们错过了重要的约束设置…… 问题 通过使用锚,我们可以很容易地通过将一个锚连接到另一个锚来生成约束。 但是在这里,我们失去了像镜头所使用的流程那样通用和通用的功能。 解决方案:让我们定义另一个构建器。 我创建了一个名为FunctionalBuilders的库,该库包装了约束构建并使其使用起来更加美观。 […]

如果您要使用动态尺寸的集合视图单元格,可以按照以下两个简单步骤操作:

具有动态大小的UICollectionViewCell 如果您要使用动态尺寸的集合视图单元格,可以按照以下两个简单步骤操作: 在控制器类中设置UICollectionViewFlowLayout的EstimatedItemSize属性。 在您的UICollectionViewCell子类中实现preferredLayoutAttributesFitting方法。 3.步骤2的替代方法是使用下面的类作为UICollectionViewCell子类的基类。 请评论您的改进建议。

iOS开发参考-模糊与活力

我们经常需要在图像视图上模糊或在任何动态背景上模糊视图。 有两种方法可以完成此任务。 1.进行具有所需模糊效果的模糊视图,并添加任何标签。 2.具有模糊视图和鲜艳度视图。 然后在这些内容之上添加一些内容,例如文本。 添加模糊视图– 让blurEffect = UIBlurEffect(style:.regular)让blurEffectView = UIVisualEffectView(effect:blurEffect)blurredEffectView.frame = view.boundsview.addSubview(blurredEffectView) 添加标签以模糊视图 让tLabel:UILabel = UILabel()tLabel.text =“早上好” tLabel.font = UIFont(名称:“ Futura”,大小:30)tLabel.textColor = UIColor.white.withAlphaComponent(0.6)tLabel.frame = bluredEffectView。 contentView.boundsblurredEffectView.contentView.addSubview(tlabel) 添加活力和标签 让vibrancyEffect = UIVibrancyEffect(blurEffect:blurEffect)让vibrancyView = UIVisualEffectView(effect:vibrancyEffect)vibrancyView.frame = bluredEffectView.contentView.boundsvibrancyView.contentView.addSubview(tLabel)tLabel.frame = vibrancyView.contentView.contentView.contentView.contentView.contentView.contentView.contentView。 view.addSubview(blurredEffectView) 参考文献 RayWanderlich的UIVisualEffectView教程 使用UIVisualEffectView 关于模糊和鲜艳度的StackOverflow问题 OmniDev网站的模糊和鲜艳度指南 用Swift编写有关模糊和活力的文章