Tag: Objective C

为iOS创建自定义键盘

每个开发人员都希望在创建产品时尽最大努力。 通常,它需要超越自然设计的新颖感。 幸运的是,应用程序扩展允许开发人员在常规应用程序的边界之外添加自定义功能。 此扩展名的类型很少,其中一种是自定义键盘。 从iOS 8开始,开发人员可以使用此功能扩展其移动应用程序。 系统键盘很棒,很舒适,而且功能齐全,但是在某些情况下,第三方键盘可以通过滑动或预测用户意图等功能来加快文本输入速度(这是一种什么样的法术?)。 引入它时,我的第一个想法是创建一个简单的密码管理器。 在小型非物理键盘上输入复杂的密码从未如此简单。 从理论上讲,我只需要构建自己的自定义键盘并从此过上幸福的生活。 但是,后来我了解到某些并发症,例如… 自定义键盘限制 这种局限性可以用一句话来概括:不允许自定义键盘访问标准应用程序通常可以使用的许多功能。 首先,关于我的密码管理器,Apple担心用户隐私。 这就是为什么我们在与安全字段进行交互时不能使用自定义键盘的原因,因此无法创建带有魔术按钮的键盘来为我们完成所有这些密码键入操作。 此外,您不能在普通键盘框架上绘制任何元素。 真可惜,但是它与Apple的人机界面设计规则很好地结合在一起,就是这样。 除此之外:我们无法通过“自定义键盘”访问麦克风和摄像头,这使得听写输入变为不可能。 正如您所期望的那样,该列表还在不断增加。 您可以在《人机界面指南》中阅读有关限制和良好实践的更多信息 ……和必备 必须提供一个按钮以切换到其他键盘或返回普通键盘。 在系统键盘中,是那个神秘的地球仪图标按钮。 为了不干扰用户的期望,习惯和根深蒂固的行为,我建议保持这种状态。 我们需要做的就是将带有所有事件的addTarget添加到我们的键盘UIInputViewController到方法advanceToNextInputMode()中(用户可以点击此按钮以快速切换到下一个按钮,或者点击更长的时间以查看可用键盘的列表)。 要使用Internet连接,用户必须允许完全访问自定义键盘。 它允许键盘与包含的应用程序共享数据。 因此,该应用程序可能成为键盘的管理器应用程序。 因此,它可用于同步用户词典或调整并保留用户首选项。 但是,键盘必须在没有完全访问权限的情况下始终可用,因此也需要没有互联网连接。 实施技巧 只是一些代码片段 我不想逐步介绍您,因为我确定您知道如何创建xcode项目,添加视图等。要开始,您需要添加一个新目标(文件->新建->目标->自定义键盘扩展名)到现有项目。 您将获得InputViewController子类,它是键盘的起点-您可以在此处添加控件。 让我们跳到很酷的东西。 例子在Obj-c中,但是基本上它们没有任何特定于语言的怪癖,如果您使用Swift编写,翻译它们会非常容易。 我还建议阅读Krzysztof Pelczar文章中的更多实施技巧。 处理回车键类型和键盘类型 如果要创建可用作主键盘的应用程序,则需要准备不同的文本输入方案。 为了方便用户,您可以在用户输入电子邮件地址时添加“ @”按钮而不是其他按钮,或者在编辑仅允许数字的字段时仅显示数字键盘。 您可以通过在InputViewController中调用来获取当前的键盘上下文(UIKeyboardType): 我不建议使用可视界面构建器来放置按钮,因为完成该过程将花费更长的时间,并且从长远来看将变得很难维护。 通常,在我们的项目中,我们使用具有布局约束(可视格式语言-FTW!)的代码创建视图。 但是这次我决定计算每个键盘按钮的帧将是更好的解决方案。 它只会被计算一次,不需要在相邻视图之间保持约束,并且可以非常快速地调整大小。 一段代码只是为了展示它是如何完成的: 处理长按按键重复 当用户点击删除按钮时,他希望它将删除文本,直到他抬起手指。 这就是它在本机键盘上的工作方式,这也是根深蒂固的用户行为之一。 但是有一个缺陷。 据我所知,没有手势识别器会在每个给定的时间间隔被调用。 但是我们可以使用UILongPressGestureRecognizer和NSTimer做一个简单的技巧。 结果,当用户将手指放在删除按钮上时,我们每150毫秒接收一次事件。 […]

iOS / Swift中的OpenCV入门

对于不认识的人,OpenCV是计算机视觉开放源代码库。 它是用C ++编写的,这意味着您几乎可以在任何地方使用它。 从桌面应用程序到移动应用程序。 简而言之,OpenCV是: OpenCV(开源计算机视觉库)是一个开源计算机视觉和机器学习软件库。 OpenCV的构建旨在为计算机视觉应用程序提供通用的基础结构,并加速在商业产品中使用机器感知。 作为BSD许可的产品,OpenCV使企业可以轻松地使用和修改代码。 尽管创建新项目并添加pod或框架不是通常的任意任务,但考虑到它的好处,这是一个相当简单的过程。 首先,您可以克隆包含我们将在此处执行的操作的存储库。 在这个项目中,我们将从手机的摄像头获取视频流,并对其应用一些简单的色彩效果,仅用于演示目的。 因此,您需要将必需的权限添加到您的info.plist文件(即NSCameraUsageDescription 。 我们将添加OpenCV框架并将其与Swift一起使用。 首先创建一个项目(du!)。 然后初始化pods: $ cd project_root_directory $ pod init 然后将框架添加到您的Podfile : pod’OpenCV’,’〜> 3.1.0.1′ 不要忘记打开由CocoaPods创建的工作区,而不是打开您创建的项目。 OpenCV对称为Mat的n维密集数组数据类型(而不是UIImages 。 因此,我们需要一种在两者之间进行转换的方法。 为此,我们UIImage添加扩展 UIImage + OpenCV.h UIImage + OpenCV.mm 不要错过.mm扩展名。 它表明它是一个Objective-C ++文件,因此XCode以此进行编译。 否则它将无法正常工作。 接下来,我们将创建一个类,用于从相机获取图像流以进行处理: OpenCVCam.h OpenCVCam.mm 上面的类初始化相机,并处理每一帧。 它没有做任何花哨或复杂的事情,但对于初学者来说,这很好。 好了,图像已处理。 现在怎么办? 现在,我们必须在视图控制器中获取图像,或者将其实际使用或呈现。 为此,我们定义了一个委托(在OpenCVCam.mm ): OpenCVCamDelegate.h 到目前为止并不难,不是吗? 好的,我们差不多完成了。 不过,有一个很小的问题。 […]

如何使用Appcode和Swiftify加快应用程序到Swift的转换

Swiftify提供了多种将Objective-C代码转换为Swift的方法。 如果您使用AppCode而不是Xcode进行开发,则可以使用Swiftify的AppCode插件直接从编辑器中转换代码行或整个文件的选择。 优点 AppCode的可扩展性使其成为一个理想的IDE,可将您的Objective-C项目逐步迁移到每个文件的Swift。 它使您能够: 同时转换.h和.m文件对,与Swiftify的Xcode扩展名不同,由于Xcode Source Editor扩展名的限制,Swiftify的Xcode扩展名一次只能处理一个文件。 借助内置的AppCode项目文件解析器,自动更新Xcode项目文件。 无需离开AppCode或使用外部工具即可完成整个转换。 安装插件 要安装Swiftify AppCode插件,请转到AppCode菜单,然后选择首选项 。 从侧面板中选择插件 ,然后选择底部的浏览存储库按钮。 搜索“ Swiftify”,然后单击“安装”按钮。 最后,重新启动AppCode以开始使用插件。 入门 在开始将代码转换为Swift之前,您需要添加API Key 。 转到Swiftify扩展页面,然后复制您的API密钥。 返回AppCode,打开AppCode> Preferences ,选择Tools ,然后快速化并粘贴您的API密钥。 在AppCode中打开您的应用,然后选择一个Objective-C类开始对其进行转换。 将Objective-C类转换为Swift AppCode插件可让您将代码行或整个文件中的一部分转换为Swift。 选择一些Objective-C代码,右键单击它,然后选择Swiftify> Convert Selection to Swift 。 您也可以通过单击“ 工具”菜单,然后选择“ Swiftify”来访问相同的选项。 要转换整个类,请从项目窗口中选择.h和.m文件,右键单击它们,然后选择Swiftify> Convert [FileName] to Swift 。 Swiftify将创建一个新文件,其中包含来自Objective-C标头的所有类和方法以及您选择的实现文件。 请注意,在这种情况下,Swiftify不会生成Objective-C桥接标头,因此,如果新生成的Swift代码使用Objective-C类,则需要手动创建桥接标头。 为了能够构建项目,请选择旧的.h和.m文件,右键单击它们,然后选择从Xcode项目中排除 。 这将从项目文件中删除对这些文件的引用,但会将它们保留在项目目录中,以防您需要再次返回它们。 确认一切正常后,可以继续从项目目录中删除旧的.h和.m文件。 选择下一个要转换的Objective-C文件,然后执行相同的步骤。 如果遇到错误,表明您的项目对刚转换的类的某个体系结构具有未定义的符号,请确保选择“运行”>“清理构建文件夹” ,然后再次构建应用,以进行清理构建 […]

如何保持您的iOS本地化文件整洁-Swift脚本版本

与bash脚本类似,我们首先通过create()从根目录中递归搜索本地化的字符串文件。 Apple的FileManager通过枚举对象使文件搜索变得容易。 注意,由于我们通常将打包在本地化文件中的Pod本地化文件打包,因此我们将忽略它们。在parse(_ path: String)方法中的本地化文件并存储时,我们将在此处搜索键重复来优化脚本。 Set的键。 该方法将为所有重复的密钥以及找到的文件路径打印error 。 ( 注意:任何 print(“error:”) 或 print(“warning:”) 格式都会通知Xcode在工具栏上显示相应的消息以及其他构建失败和警告 ) 接下来,我们将验证validateMatchKeys方法中所有可本地化的字符串文件中的键是否匹配。 我们将使用symmetricDifference编织出任何与基本文件都不匹配的额外键。 如果任何可本地化的文件不包含相同的键,则此方法将引发错误并中止脚本。 这使您有机会在下一次执行cleanWrite()之前清理可本地化的文件,它会使用已排序的键并删除多余的空格和换行符来重写可本地化的文件。 下一步是搜索代码库,存储这些文件中使用的密钥,并根据我们的基本密钥进行验证。 有了一点正则表达式,我们就可以成功找到密钥并将它们和文件的路径存储在LocalizationCodeFile结构中,同时还可以通过紧凑地映射出没有密钥的任何文件来优化脚本。 一旦有了我们的结构,我们将点击validateMissingKeys() -将遍历我们的LocalizationCodeFile集合,并减去基本键的每组键。 此结果中的任何其他键都不是我们基本键的子集,因此会给我们丢失的键。 如果找到任何丢失的键,则会引发错误。 我们的最后一个策略是搜索任何死键,这些死键是在可本地化文件中定义但未在我们的代码库中使用的键。 我们可以通过从flatization从LocalizationCodeFile集合中收集的所有键减去基本键来检索死键。 此处找到的所有键均显示为警告,因为此步骤更多是建议清除技术债务,而不是影响问题的用户。 对具有600多个可执行文件和估计800多个密钥的企业代码库执行bash脚本平均要花费81秒 。 在相同的代码库上运行此swift脚本平均要花3秒 ! 🚀🙌 保持干净 ,可读的代码库应该是每个工程师的目标。 通过将此Swift脚本注入到您的后期构建中,清理和维护可本地化的文件应有助于实现该目标并感到自动化。 您可以阅读我以前的博客,以了解如何将脚本注入到Xcode构建后编译中( https://buildingvts.com/clean-ios-localizable-files-8b910413b985 )。 我还在其中包括了一个包含整个脚本的GitHub项目以及此处的示例项目:https://github.com/ginowu7/CleanSwiftLocalizableExample。 随时发表任何意见或建议,您可以在Twitter @ ginowu07上关注我! 编码愉快! 🙏

在您的Swift应用中运行时内省和修改类

介绍ObjectiveKit 还记得2016年5月的重大动态辩论吗? 如果没有,请快速阅读以下内容: 动态迅捷 布伦特·西蒙斯(Brent Simmons)(如果有不清楚的话,请参见存档):在最近和将来的文章中,我正在记录问题…… mjtsai.com 简而言之,争论是开发人员目前正在利用ObjC运行时的动态功能解决许多类型的问题。 当我们(可能)朝着Swift完全取代Objective C的未来迈进时,Swift将需要提供自己的本机动态解决方案。 在iOS和macOS上,至少在可预见的将来,Swift将在Objective C运行时之上运行,这意味着我们可以完全访问这些功能。 可悲的是,在Swift中使用它们确实很痛苦。 我们可以做得更好…… 你好ObjectiveKit ObjectiveKit是用于访问Objective C运行时函数的新Swift框架。 它使自省,修改方法和在运行时创建类之类的事情变得非常容易,以一种对Swift来说尽可能原生的方式进行。 它是如何工作的? 您需要做的就是初始化一个ObjectiveClass对象,该对象的类型是您要内省/修改的类,然后使用ObjectiveKit的众多功能之一。 这是一个简单的示例: 使用Objective C运行时既有趣,有教育意义,又对调试非常有用。 但是,这也很危险-在生产中使用前请格外小心。 检查一下: github.com/marmelroy/ObjectiveKit

NSTimer代表NoSwiftyTimer

NSTimer是Apple提供的创建计时器的最简单机制之一。 您可能已经看到它适用于多种用途:在一定的延迟后显示或隐藏视图,定期更新的视图或周期性或延迟执行的任何其他任务。 毫无疑问,它们是非常通用且功能强大的工具。 NSTimer基本上只是在等待特定时间间隔之后,然后将特定消息触发到其目标。 如您所见,我在这里谈论消息和目标。 这是第一个问题出现的地方。 目标和消息听起来不是很迅速,对吗? 最近,我不得不使用NSTimers,我发现了一些令人讨厌的细节。 首先,正如我们所看到的,它具有基于Objective-C运行时的非常古老的时尚语法,使用目标和选择器而不是闭包,这使它们在我的Swift代码中看起来像一条鱼。 另一方面,更重要的是,如果您不关注ARC,则NSTimers是在应用程序中引入内存泄漏的最简单方法之一。 为什么? 好吧,请检查以下示例: 从NSTimer到SwiftyTimer 苹果在NSTimer的文档中留下了非常明确的信息: 子类化注释 :您不应尝试将NSTimer子类化。 由于这个原因,我决定使用组合来创建我的自定义Swifty版本的NSTimers。 基本上将它们包裹起来,将其讨厌的部分扫到地毯下。 但这将使我能够解决这两个问题,获得更快捷的语法并更容易避免内存泄漏。 使用这个简单的包装器,我们能够以非常简单且易读的方式避免来自计时器的强烈引用。 再一次,如果您将此代码复制到操场上,您将看到它现在生成另一个输出: 你好 再见 多亏了附加层,我们得以避免内存泄漏,因为NSTimer现在保留的不是使用计时器的代码,而是SwiftyTimer的实例。 当然,我们在SwiftyTimer和NSTimer之间仍然有一个保留周期,但是通过使用SwiftyTimer的invalidate方法,我们可以轻松打破这一局面。 在这一点上,快速化定时器的使用,使用参数的默认值并利用闭包是一项非常容易的任务。 最后获得一个更具可读性和更少错误的解决方案。 结论 Apple可能(并希望)很快会给仍然需要它的那些API带来所有的爱和温柔,但是与此同时,它们是创造力的好借口,因此获得了更具可读性和安全性的代码……害羞! 如有任何疑问,请随时在github,twitter或dcordero.me上添加我。

iOS TableView使用Swift预取数据源

Apple引入了用于在iOS 10中预取UITableView和UICollectionView的 API。这是有关如何实现UITableViewDataSourcePrefetching协议的简短故事。 总览 为了实现预取,我们向视图控制器确认了UITableViewDataSourcePrefetching协议,就像UITableViewDataSource和UITableViewDelegate一样 。 这使表视图的数据源可以在调用tableView(_:cellForRowAt 🙂数据源方法之前开始为单元格加载数据。 入门 将视图控制器设置为表视图预取数据源 tableView.prefetchDataSource =自我 在tableView(_:prefetchRowsAt :)的实现中,初始化指定索引路径中单元格所需的数据的异步加载。 func tableView(_ tableView:UITableView,prefetchRowsAt indexPaths:[IndexPath]){ 用于indexPaths中的indexPath { 警卫让_ = loadingOperations [indexPath]否则{返回} 如果让dataLoader = dataStore.loadPhoto(at:indexPath.row){ loadingQueue.addOperation(dataLoader) loadingOperations [indexPath] = dataLoader } } } 当表视图通知您tableView(_:cancelPrefetchingForRowsAt 🙂方法不再需要数据时,取消挂起的数据加载操作 func tableView(_ tableView:UITableView,cancelPrefetchingForRowsAt indexPaths:[IndexPath]){ 用于indexPaths中的indexPath { 如果让dataLoader = loadingOperations [indexPath] { dataLoader.cancel() loadingOperations.removeValue(forKey:indexPath) } } } 异步加载数据 与tableView(_:cellForRowAt […]

有效地将UISearchController与UICollectionView结合使用

使用UITableView可以轻松集成UISearchController,而使用UICollectionView则不容易 问题 苹果公司的iOS类UITableView与UISearchController及其UISearchBar配合得很好,但是,几乎所有开发人员在尝试将搜索栏与UICollectionView对象进行可视化集成时都举手示意。 在开发负责显示大型组织的REST服务的照片缩略图的应用程序时,我发现了这个问题,并且我希望拥有与UITableView相同的布局。 基于StackOverflow和其他发布内容,大多数人要么不理会试图直观地集成类,要么重载了节标题。 解决方案 由于我希望搜索栏以动画方式显示和隐藏,并在UICollectionViewController的上下文中与相关的集合视图保持正确的尺寸,因此这比我期望支持iOS 9的工作量要多得多和10,但我确实为我的需要制定了解决方案。 为了实现这一点,我编写了一个名为DSSSearchController的UISearchController子类,该子类添加了许多用于显示和隐藏搜索栏的方法。 我将其设计为可与任何视图一起使用,但仅使用特定的集合视图对其进行了测试。 在下面,您将找到Objective-C标头和源代码以及Swift中的版本。 如您所见,需要大量代码来确保搜索栏相对于集合视图的大小正确。 在iOS 9和10中,Apple似乎不将AutoLayout与由UICollectionViewController管理的集合视图一起使用,因此这就是为什么使用框架进行大量计算的原因-试图使用AutoLayout被证明是有问题的。 早期,我发现某些动画会产生一些我不喜欢的搜索栏重影或覆盖,因此这就是为什么某些操作按其原样进行排序的原因。 为了使用我的类,我编写了一个名为configureSearchController的方法,该方法在viewDidLoad期间调用以初始化该类并在搜索栏上设置一些选项。 在我的原始代码中,获得searchBar属性后,将其barTintColor更改为深海蓝色,将tintColor更改为白色,并将其搜索字段的tintColor更改为深海蓝色。 另外,我的导航栏的barStyle属性具有UIBarStyleBlack。 当用户点击导航栏中的放大镜图标时,将调用toggleSearchBar:方法。 我发现当我要展示一个视图控制器时,我需要知道我的搜索控制器是否是要展示的控制器,这是在搜索字段具有焦点时发生的。 我确定我的解决方案可以改进,并且可能会在更高版本的iOS中中断,但目前可以使用。 希望iOS 10的后继版本将内置一个解决方案。

使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,*)){ […]

Mobven的iOS环境

曾几何时,iOS开发工具相当简单且静态。 目标C是一门成熟的语言,它基于良好的旧C语言。Cocoapods仅被接受为依赖性管理器。 然后斯威夫特发生了。 没有指针,没有分号,没有.h文件。 它为不了解C / C ++或Java的人提供了一个避风港。 Swift 1.0是一个有趣的工具。 表/集合视图不再是痛苦的来源,项目变得整洁,应用程序原型制作时间大大减少。 但是,每次更改都会带来混乱。 斯威夫特也是如此。 语法不稳定(对那些经历过2.0移植的人们是有帮助的),缓慢的编译时间(如旧的Eclipse经验),错误的语法荧光笔(很抱歉,不是错误的,不能正常工作)和不眠,咖啡因激发的工程师是我最早写的。 Swift是从多年经验中提炼出来的产品。 最终,它将在许多领域取代Objective-C,但今天不是那天。 因此,为了不造成令人尴尬的生产失败和小故障,我们坚持使用旧的Objective-C,并在实验室实验中限制Swift。 在Mobven,我们创建了一个应用程序开发流程,以赋予开发人员自由的空间。 开发过程非常简单。 该代码与单元测试一起编写。 然后,一位资深人士与原始作者进行审查。 如果一切正常,则将代码合并到主分支(称为dev)中。 CI系统使用Fabric和周期结束来构建,测试和发布。 为了保持循环运动,工具必须固定且可靠。 这是Mobven iOS团队提供的5个基本开发工具。 Git:源代码控制是工程师必不可少的工具。 这样可以防止出现“新文件夹(42)”的情况。 而且,它使开发人员可以在同一代码库上工作。 Git旨在管理巨大的代码库,并且可以完美地完成其工作。 Jenkins CI: Jenkins提升了周期的重量级。 首先,它提取代码,运行测试,创建IPA并将其分发。 其次,它检查每个推送分支的代码质量和代码覆盖率。 许多开发人员讨厌编写像文档这样的单元测试,但这是整个应用程序的故障保护。 防止将新的错误引入代码库是一项巨大的优点,并且可以节省时间。 Cocoapods:作为依赖项管理器,Cocoapods的工作方式类似于Ruby Gems。 它可以正常工作并节省大量时间。 我们在内部和公开使用我们的应用程序和框架。 捕获:在应用程序开发中,创建错误场景是一项艰巨的任务。 借助Capture,我们的质量检查团队可以轻松指出错误的UI实施和崩溃情况。 动量:这种内部框架使我们能够毫不费力地重新创建错误方案。 质量检查团队还将其用于自动化过程测试。 总之,没有一种创建移动应用程序的真正方法。 我们的团队始终如一,纪律严明,堪称典范。 动量和捕获是它的产物。 坎纳·塔塔尔(Caner Y.Tatar) 软件工程师 http://www.mobven.com