Tag: uikit

UIKit:查看阴影

上次我们讨论了如何使视图的角变圆。 今天,按照承诺,我将向您展示如何使用layer.shadowX属性,以使视图看起来像Card,并获得其他炫酷效果。 让我们从简单而永不过时的Card View开始。 我知道你需要这个 myCard.layer.cornerRadius = 10 myCard.layer.shadowOpacity = 1 myCard.layer.shadowColor = UIColor(名称:“ Swift Orange”)!. cgColor myCard.layer.shadowRadius = 4 myCard.layer.shadowOffset = CGSize(宽度:4,高度:4) 该代码是不言自明的: 我按照上cornerRadius UIKit Rounded Views中的描述设置了cornerRadius属性。 我将阴影不透明度设置为从0到1的值,以将其应用于阴影颜色。 我设置阴影颜色。 我设置代表模糊半径的阴影半径。 如果您不愿意像我一样阅读,则只需要知道该值越高,图像就会越模糊。 如果您想学习,请单击此处。 最后,我设置偏移量,该偏移量是阴影相对于以点表示的图层的偏移量。 注意:要使阴影可见,请不要将layer的属性masksToBounds设置为true,否则它将被裁剪。 这还没有结束……还有一个阴影属性可供我们使用,以产生一些很酷的效果: shadowPath。 让我们看看如何制作一个像漂浮在设备屏幕上的视图。 让cardWidth = myCard.frame.width 让cardHeight = myCard.frame.height 让distanceFromCard = 15 myCard.layer.cornerRadius = cardWidth * 0.5 myCard.layer.shadowOpacity = 0.1 […]

在UIkit中创建iOS自定义视图

最初 由 Andres 在2017年10月17日 发布在 www.scalablepath.com 上。 我刚从大学毕业就开始使用iOS。 使用不熟悉的平台和编程语言是一项挑战。 真正使我紧张的是什么? 是创意团队要完成的设计。 出色的设计可以为网站注入新的活力,但它越独特,就越有可能需要在UIKit中创建iOS自定义视图。 作为一个新开发人员,这可能会令人生畏。 本文的目的是解释如何将任何设计转换为功能用户界面元素。 iOS定制视图 视图是应用程序用户界面的基本构建块。 通常,您可以通过将故事板中的视图从库中拖动到画布来在其故事板上创建视图。 但是,有时您需要使用UIKit中的标准“标签”或“按钮”元素创建一个不可用的元素。 这是您需要自定义视图的时间。 顺便说一句,如果本文中的任何术语听起来都不熟悉,您可能需要查看Apple的UIKit文档。 在本文的其余部分,我将假设您具有UIKit和Swift的使用知识。 初始设计 让我们从选择无法在标准UIKit视图中重新创建的设计开始。 我找到了由耿高设计的“圆形进度栏”,它非常适合该法案。 此元素由两个文本标签(标题和副标题)和一个圆形完成指示符组成,该指示符旨在在任务进行时填充灰色轨道。 我们将从创建一个新的Xcode项目并将其命名为CircularProgressBar开始。 文件>新建>项目>单视图应用程序 当我们将创建自定义UIView时,我们需要使用新类CircularProgressBar扩展UIKit的默认UIView。 导入UIKit class CircularProgressBar:UIView { } 有两种向设计中添加标签的方式:通过代码或使用.xib文件。 为了避免进一步的混乱,术语“ xib”和“ nib”通常可以互换使用。 NIB来自“ NeXTSTEP Interface Builder”,这是苹果现已停产的操作系统。 虽然.nib文件已替换为.xib文件,但开发人员仍将其称为“ nibs”。 我喜欢使用.xib文件创建我的iOS自定义视图,因为它们需要较少的编码并且更容易进行更改。 因此,我们执行此操作,并将其命名为CircularProgressBar。 文件>新建>文件,然后选择查看 我们将在导航器中选择CircularProgressBar.xib,然后将.xib的文件所有者定义为我们的类扩展名:CircularProgressBar 等一下,什么是文件所有者? StackOverflow提供了比以往任何时候都更优雅的解释: “文件所有者是一个实例化的运行时对象,在加载.nib时,它拥有.nib的内容及其出口/动作。 它可以是您喜欢的任何类的实例。” 有了这些了解,我们将隐藏状态栏,并将.xib的大小设置为“自由格式”。 这样,我们可以更改尺寸,以便视图具有与设计相似的尺寸。 在这种情况下为300 x […]

为iOS打开Sourcing ReactiveLists

在过去的几年中,PlanGrid iOS团队构建了许多内部API,使我们能够更快,更一致地发布新功能。 今天,我们将公开发布第一个组件-您现在可以在GitHub🎉上找到它。 我们的团队有许多开放源代码爱好者,并且我们还使用了大量开放源代码库,因此我们一直计划随着时间的推移发布这些组件。 使用UIKit进行声明式UI编程 ReactiveLists是一个类似React的API,建立在UITableView和UICollectionView 。 该API使我们能够编写无状态函数来生成用户界面。 根据我们的经验,这些无状态函数比*DataSource和*Delegate类型更容易理解,后者是向UIKit组件提供数据的标准模式。 API的用法通常类似于下面的示例,您编写了一个将数据映射到ReactiveLists视图模型的函数,从而产生了一个非常易读的无状态函数: 这不仅使UI代码易于阅读和迭代,而且使其易于测试。 我们的团队成员Benjamin Encz在他的演讲“将UIKit内外翻”中深入讨论了这种声明性UI编程方法的优势。 从封闭到开放 我们团队中有很多人为开源项目做出了贡献,或者从头开始创建了自己的项目。 事实证明,将在现有代码库中演化的组件开放源代码采购的经验大不相同。 我们花了几天的工程时间将组件移至其自己的Swift框架中,删除了对核心应用及其单元测试助手的依赖,将ReactiveSwift集成移至扩展中,并花费大量时间重命名API,改进文档和重构了测试套件。 最重要的是,我们构建了一个示例应用程序,使该库更易于理解。 这是开放构建的另一个好处-它提高了在我们代码库的大部分中使用的组件的数量。 准备生产,但不准备1.0.0 我们没有等到该库完成后才将其发布给公众,因为我们相信它在当前状态下会增加很多价值。 我们发布的版本是已经在PlanGrid iOS应用程序中发布了近两年的版本。 因此,我们绝对认为该库可用于生产。 也就是说,我们期待大量改进,并且API不断变化,因此将此版本发布为0.1.0 。 结论 我们认为这是与iOS社区共享一些内部API的第一步。 我们每天都看到利用开源软件的好处,并且很高兴能够做出贡献。

CocoaTouch:框架和边界

用户界面是应用程序开发中最重要的部分之一。 我们的工作是创造出色的用户体验,以与其他应用程序脱颖而出。 但是,让我们先深入基础知识。 对于UI组件,我们必须考虑两件事: 我的元素有多大? 应该放在哪里? 我的元素有多大? 您必须考虑UI元素的宽度和高度 。 Core Graphics为该用例提供了一个结构。 CGSize结构{ CGFloat宽度; CGFloat高度; }; 应该放在哪里? UI元素使用坐标系定位。 这开始于(在iOS中)屏幕的左上角。 用CGPoint结构指定位置。 CGPoint结构体{ CGFloat x; CGFloat y; }; 两者在一起 现在我们有了大小和位置。 两者结合形成一个“矩形”。 还有一个同时包含信息的结构,称为CGRect 。 struct CGRect { CGPoint起源; CGSize大小; }; 仔细研究源代码,我们将看到矩形是一个包含已知结构(CGSize和CGPoint)的结构 。 帧 让我们创建一个新的UIView。 将矩形(大小和位置)指定为CGRect并将其作为参数传递给构造函数。 let rect = CGRect(x: 70, y: 70, width: 160, height: 100) let myView = […]

自定义UIViewController模态过渡动画

许多自定义过渡教程深入介绍了演示控制器, 模态和导航 控制器案例,并需要下载示例项目(ANGTFT)。 这是自定义过渡的简单,简明的介绍,可以用最少的散文直接跳转到代码。 免责声明:为简洁起见,主视图控制器遵循所有必要的转换协议。 同样,虽然在实践中可能不适合使用强制转换和扩展,但它们还是宽松的。 我们将创建一个简单的滑动抽屉动画,该动画在两个视图控制器之间转换(请参见上图)。 MyViewController是应用程序的根视图控制器,由一个按钮以模态方式呈现抽屉的视图控制器: 第二个视图控制器是DrawerViewController ,其中包含一个用于关闭视图控制器的按钮。 必须使用MyViewController的 onButtonTap方法中的这两行来替换标准的模式转换,该模式转换通常会将显示的视图控制器从底部向上滑动。 抽屉视图控制器.modalPresentationStyle = .custom抽屉视图控制器.transitioningDelegate =自我 注意:未能设置modalPresentationStyle将导致使用标准模式过渡,而不管是否设置了transitioningDelegate 。 为了将MyViewController分配为DrawerViewController的自定义过渡动画的提供者,它必须符合UIViewControllerTransitioningDelegate协议。 在展示或关闭DrawerViewController时,UIKit将调用这些方法。 如果需要使用不同的自定义动画进行演示和关闭,则可以返回不同的动画控制器 。 例如, MyFadeInTransitionAnimator,MyFadeOutTransitionAnimator。 为了简化此示例, MyViewController遵循UIViewControllerAnimatedTransitioning 。 启动过渡并提供动画控制器后,UIKit将调用以下UIViewControllerAnimatedTransitioning方法: transitionDuration(transitionContext 🙂返回过渡动画的持续时间。 设置聚合动画的持续时间时,应该由animateTransition方法使用。 animateTransition(transitionContext 🙂 实现自定义过渡动画。 transitionContext包含对参与视图控制器的引用。 它还包括一个containerView ,必须将所有要显示的视图添加到其中。 符合UIViewControllerContextTransitioning。 调用transitionContext.completeTransition(_ 🙂非常重要 当过渡动画完成时,因为UIKit不允许您的应用在过渡进行中接收触摸事件 。 而已! 自定义过渡动画-Apple Developer Docs 自定义UIViewController过渡— Ray Wenderlich iOS自定义过渡教程-The Swift Dev

重新构建新的App Store应用程序-Today视图

阴影 App Store应用程序中的每张卡片都有一个柔和的阴影,略微的垂直偏移使它具有深度感,并向用户建议可以轻按卡片以打开其详细的故事视图。 在当前的App Store应用中,此阴影似乎只是静态阴影。 我决定不仅要克隆此阴影,还要进一步使用Core Motion根据设备的Pitch(水平倾斜)和Roll(垂直倾斜)移动阴影。 这将以一种微妙的方式使界面感觉更生动,更丰富,类似于在tvOS界面上所做的方式。 长按手势 在当前的App Store应用程序中,每个Card视图本身也是固定的,并且不会过度交互。 您可以点按卡片以切换到“故事”详细信息视图,但不能以任何其他方式与卡片交互。 就像我可以使用tvOS界面一样,作为用户,我不得不触摸和操纵卡片,而不仅仅是点击。 我通过执行长按手势来改进此功能,该手势可以在按住卡时将其略微缩小。 这扩展了阴影创建的深度隐喻,但也不过分,以至于使卡片感觉过于灵活和不现实。 iPad网格布局 这些卡的布局会有所不同,具体取决于您是在iPad还是iPhone上查看新的App Store应用。 在iPhone上,您会看到一列垂直的卡片,其宽度和高度都完全相同,而在iPad上,您会看到两列的单元格,每个单元格将在压缩宽度和扩展宽度之间交替,以提供更多网格布局,可以更好地利用iPad的更大屏幕空间。

让我们停止对ViewController长度的教条

你好 我叫Ted Bendixson,我创建了其中包含200多行代码的View Controller。 我试图掩饰多年的耻辱。 你知道是怎么回事。 您参加一个聚会,发现自己与朋友和同事随意地开玩笑。 然后,您甚至没有考虑,就不小心提到了UserProfilesTableViewController的第203行。 面孔变白。 有人将酒倒入抛光的硬木地板上。 使用MVC和MVVM,您绝不应该有任何具有超过200行代码的视图控制器,对吗? 如果您使用反应性可可,则可以将这些视图控制器的尺寸缩小到更荒谬的尺寸。 那我为什么敢这样做呢? 我迷失了方向吗? 如果您像我一样,那家伙写的那个项目就遭到了一个人的折磨,后者大声地反对在多个ViewController之间传递数据的需要。 您清理了他的烂摊子,发誓永远不要让这种情况再次发生。 但是也许您对清除过去的热情太过热情了。 您的Tiny ViewController Obsession是否提高了您的生产率,还是有碍于提高生产率? 视图控制器存在的原因 在尝试回答此问题之前,我们应该首先了解ViewController试图解决的问题。 为什么我们应该首先使用具有多个视图控制器的应用程序? 通过采用这种方法,我们可以获得什么? 我曾经认识一个初级开发人员。 他不了解在控制器之间传递数据的概念。 他认为UIKit是对开发人员的一种强加手段,迫使我们创建所有这些复杂的体系结构只是为了在屏幕之间共享数据。 他更喜欢将所有代码放在一个ViewController中,因为它更易于理解。 你不能把这些东西编起来。 他只有一个ViewController拥有许多生产应用程序。 所有的UI都是用代码完成的。 要转到其他屏幕,他必须为屏幕上的一个视图设置动画,并用另一个视图替换。 在任何给定时刻,屏幕左侧或右侧都有数十个视图。 他还使用了Interface Builder,这令人困惑,因为您永远无法在他的一个ViewController中找到任何UI元素。 这是一大堆视图,按钮,表格视图和选择器,它们彼此堆叠在一起-IBOutlets相互连接在一起。 最终,他看到了光,并学会了顺其自然。 他开始使用 UIKit,而不是反对。 使用UIKit的好处 苹果公司希望我们在我们的应用程序中使用多个ViewController子类,因为这是获得UIKit原生风格的最简单方法。 如果愿意,您始终可以决定选择流氓,但您的流氓行为会付出一定的代价。 如果您决定不在您的应用程序中创建多个视图控制器,则必须开始实现UIKit免费提供的默认行为和动画。 那要花很多时间。 您也可以将初始估计值乘以10。 我们与该原生外观相关联的许多东西都直接来自UIKit。 从一个屏幕到另一个屏幕的动画,表视图,选择器,随着键盘进入屏幕而向上移动的文本字段; 全部都是UIKit。 因此,拥有ViewController这些东西的真正原因是因为我们想与UIKit一起玩得很好。 当我们与UIKit玩得很好时,我们将免费获得所有这些本机外观的行为。 换句话说,我们可以在一个月内完成一个项目,而不是十个月。 对ViewController长度有什么影响? 没有,这就是我的意思。 我们所知道的是,每个屏幕内容都需要有自己的ViewController。 没有ViewController的上帝会下雨EXC_BAD_ACCESS因为未能使该控制器低于200行而崩溃。 […]

可视化扩展布局

我注意到许多在线用户(即StackOverflow)在视图布局不正确时会跳到少数UIViewController属性,然后几乎随机地操作它们以获得所需的行为。 这总是导致对布局问题的“可接受”答案是“自动将AdjutstsScrollViewInsets设置为[true / false]”或“将edgesForExtendedLayout设置为UIRectEdgeZero”,其中一个人说“谢谢,已修复!”,另外五个人说“那没错”。修复它会有所帮助”。 即使在Apple的WWDC视频中,也没有关于这些API的工作原理以及它们相互之间有什么影响的超级清晰的解释,尽管WWDC 2013在发布时有广泛的概述。 我找不到关于其所有工作原理的出色文章,所以我想针对这些API的工作原理发布PSA,以便我们可以开始以正确的方式修复问题并停止永久保存不良信息。 我认为具有交互属性的示例应用程序绝对是了解UIKit来龙去脉的最佳方法。 如果你玩的够多的话,你就可以开始预测行为。 一旦您可以解释更改给定属性将对结果布局产生什么影响,便有了相当不错的掌握。 因此,为了说明这一切是如何工作的,我将包括一个示例应用程序的屏幕截图,然后为您提供一个停顿的地方,并思考改变给定属性的效果。 当然,您可以浏览一下,但是我想您会发现尝试提前计算出最终的布局会更有优势。 为此,我们将从iOS 10开始,它使我们可以忽略新引入的safeArea及其带来的其他API(和注意事项)的影响。 我们将在不久的将来覆盖这些内容。 这是所有具有实例化时视图/视图控制器具有的默认值以及在UINavigationController内部显示tableView时的默认外观的所有内容。 请注意,tableView的背景为绿色,但单元格背景为红色。 我们可以使用它来查看tableView的开始位置及其内容的开始位置; 当这两个不同时,我们正在查看一个非零contentInset,它显示在底部的单元格中。 这个contentInset来自哪里? 如果您检查代码,我们不会自己指定。 自动输入AdjustsScrollViewInsets 。 UIViewController属性autoAdjustsScrollViewInsets是iOS 7(对界面进行了大修的版本)中引入的,默认为true。 在iOS 7中,导航栏默认为半透明,并且为了展示半透明性,视图必须在导航栏下方开始(如果没有内容可看,您将无法欣赏半透明!)。 但是,如果视图从导航栏后面开始,则很明显,如果没有一些Apple Magic™来为开发人员处理事情,事情就会变得模糊。 这种魔术以automaticAdjustsScrollViewInsets的形式出现,它进入UIViewController的子视图并运行此测试: func magicView()-> UIView? { 保护self.isKind(of:UIScrollView.self)else { 返回self.subviews.first?.shouldApplyMagic() } 返回自我 } 并自动设置该滚动视图的contentInset以容纳navigationBar。 ContentInsets是非常基本的,我仅将这一部分包括在内,以快速提醒那些尚不清楚的人。 我发现,如果您将UIScrollViews视为内容的“窗口”,则更容易理解它们在做什么; 窗口框架本身不会改变大小,但是您可以四处走动以查看窗口框架后面的其他内容。 您所看到的不止是那里,所以一旦您触及内容的最上/最下部分,就无法再进行任何操作(忽略弹跳的UIKit给我们提供的内容)。 使用相同的隐喻,contentInset本质上将“空白”内容添加到您正在查看的内容上方,这意味着您可以看到比其他地方更多的内容。 如果我将topContentInset添加为100点,则意味着我可以滚动到内容的顶部,然后再在其上方滚动100点。 对于UITableView的基本情况,这只是空白,它将显示您的backgroundColor是什么,但是UIScrollViews可以让您做更多的事情。 我现在不会讲这个; 也许解释UIScrollViews是另一天的好主意。 既然我们对automaticAdjustsScrollViewInsets有了一个不错的了解,那么应用程序的初始状态就应该有意义。 我们的视图从UINavigationController的顶部开始(当然,它的视图)。 默认情况下,Apple会自动为我们提供AdjustsScrollViewInsets == true,因此,在布局视图时,它会看到导航栏的高度为64(进入状态栏区域),并在tableView上方为我们提供了64个空白内容。 […]

使用一流的对象创建具有多种单元格类型的UITableViews

问题 在AI公司工作时,您总是会做很多技术演示,以在野外使用某种技术。 现代算法往往具有很多您想尝试的参数,有时工作的结果只能由人类认可,尤其是当它与任何类型的图像处理/计算机视觉或AR / ML类型相关时应用程序,您必须用眼睛检查结果。 这通常导致必须创建一堆屏幕上的按钮/滑块,以便可以对核心逻辑进行参数设置,并查看新的超参数如何影响结果。 在进行了几次这样的演示之后,我决定创建一个自定义调试菜单,您可以在其中以声明方式添加几个选项,然后观察更改。 我一天通过分叉现有的调试工具Dotzu制作了原型。 项目仍处于起步阶段,但现在或多或少可以满足我的需求。 您可以在公共仓库中使用它。 以下是取得的成果的小演示: 现在,有趣的部分。 我们创建一个UITableViewController子类,在其故事板上没有单元原型: 所有的魔力都进入了它的实现。 让我们来看看: 在最底部,我们声明要呈现的模型数组 这是最有趣的部分。 由于一流的类型对象不可比,因此我们使用Swift标准库中的 ObjectIdentifier结构为每个Type对象创建Hashable / Equatable代理对象。 我们声明一个字典和两个方便的方法来注册并从中获取类型。 由于它是重复的代码部分,因此可能应将其移至称为TypeRegister或类似名称的单独类中。 3.在viewDidLoad()方法中,我们使用一个小的辅助方法将单元格类型注册到表视图中。 4.接下来,我们将关联的Model-View对注册到我们的类型字典中。 5.在cellForRowAt中:我们从数组中获得一个模型,为其确定一个单元格类型,并出列适当类型的单元格。 6.最后,我们告诉UITableView使用自动布局来计算单元格的高度,因为它可以从一个选项到另一个选项。 而已! 现在,您有了UITableView的60行代码的超灵活实现,可以处理其中无限数量的模型。 用法 框架的使用非常简单。 您所要做的就是通过调用一种方法来一次初始化调试菜单(通常在AppDelegate中): DebugMenu.sharedManager.enable() 然后以声明的方式声明您的选项(通常在控制器的viewDidLoad中 ): 通话开始的结果可以在故事开始的视频中看到 由于该仓库专用于调试,因此您可能希望使用#if DEBUG…#endif子句覆盖与DebugMenu的所有交互。 最后的话 由于UI工程不是我每天都会做的事情,因此,我很想听听您对这项技术的反馈,并希望听到关于如何做得更好的任何指导。 希望您喜欢阅读! 不要忘了在GitHub上使用仓库,并在Twitter上关注我。

将Swift编译时安全变成用户安全

发生了什么? 从技术上讲,今天没有任何事情可以阻止您作为开发人员在未经用户确认的情况下无意或无意进行不可逆的更改(例如删除用户数据)。 当然,我们尝试尽可能地减轻这种风险,在各处编写UIAlertController代码(或者为此目的甚至制作基于可爱的便捷闭包的函数),但是我们编写的API并没有采取任何措施来防止这种情况的发生。 例如,假设我们有一个类Images ,它直接注入到我们的视图控制器中 最终课程图片{ 私人var图片:[UIImage] func图片(位于索引处:Int)-> UIImage { 返回图片[索引] } func add(_ image:UIImage){ images.append(image) } func delete(imageAt index:Int){ images.remove(at:index) } } 当用户点击“删除”按钮时,我们可以轻松地编写以下代码: @objc func didPressDeleteButton(sender:UIButton){ images.delete(imageAt:currentImageIndex) } 没有编译时错误(很明显),没有警告,什么都没有-因此,我们只是删除了一部分用户数据而没有任何确认。 这是不好的。 常规的解决方案是什么? 大多数时候,开发人员会尝试在视图层解决此问题,也就是在didPressDeleteButton UIAlertController那里创建并显示didPressDeleteButton 。 当然,这通常可以解决问题,但是它有明显的缺点: 开发人员可以忘记在某些地方编写此“安全措施”代码。 尽管如此,开发人员仍然可以不小心删除图像,而不是在视图层,而是在业务逻辑中的某个位置删除图像,而无需三思而后行。 那么什么是“正确”的解决方案? 我们真正想要的是我们的Images类,根本不允许未经用户确认而执行删除操作。 为了帮助我们实现该功能,让我们创建一个UserConfirmationRequired结构: struct UserConfirmationRequired { 私人让performDestructiveAction:()->() init(destructiveAction:@escaping()->()){ self.performDestructiveAction =破坏性动作 } func performWithUserConfirmation(alertTitle:字符串,alertMessage:字符串,alertDestructiveActionTitle:字符串,完成:@转义(Bool)->()){ //检索视图控制器以显示警报 保护让窗口= UIApplication.shared.delegate?.window else […]