Tag: macos

核心动画宝石:在Swift中使用复制器层

要阅读所有代码示例的语法突出显示的帖子,请 转到Swift By Sundell ,您还可以在其中找到我的所有其他每周帖子和新播客👍 如果您曾经将像素推到Apple设备的屏幕上,则可以直接或间接使用Core Animation。 像苹果公司大多数核心框架系列一样,它是那些必不可少的技术之一,这些技术支撑着我们每天使用的众多工具-无论我们针对的是哪个平台。 尽管有大量关于核心动画的最常用API的出色教程和资源,例如使用CALayer为UIView执行更底层的渲染,或使用动画API(例如CAKeyframeAnimation ,但也有一些非常有用的尚未广为人知的功能。 在这个新的(非连续的)系列文章“核心动画宝石”中,我们将仔细研究其中的一些功能和API,以及如何使用它们来很好地解决与动画和渲染相关的问题。办法。 这周,让我们从第一个💎CAReplicatorLayer CAReplicatorLayer 。 专业化 Core Animation的图层类的体系结构非常围绕专业化的思想。 您拥有根类CALayer ,该类基本上是可以在其上绘制任何内容的通用画布,然后您可以拥有一系列专门用于某种渲染的子类。 CAReplicatorLayer专门以一种有效的,硬件加速的方式绘制原始层的多个副本(因此它是“复制者” )。 当绘制平铺背景,图案或其他应重复多次的内容时,此功能非常有用。 我什至用它来实现我即将发布的开源Swift游戏引擎的纹理平铺功能。 您的UIColor可以做到吗? 使用重复图像实现背景的一种非常常见的方法是使用UIColor ,它具有一个采用patternImage的初始化patternImage 。 它使您可以将重复的图像序列视为一种颜色,并将其用作背景,如下所示: view.backgroundColor = UIColor(patternImage: image) 在某些情况下,尽管绘制重复图像的方式可能正是您所需要的,但在其他情况下,您可能需要做一些更复杂的事情-这就是CAReplicatorLayer用武之地。 假设我们要绘制一个由重复图像组成的图案,但是我们也想用蓝色阴影对其进行着色,从而使该图案从图像的原始颜色形成渐变,直到完全着色为止,如下所示: 使用复制器层,无需任何自定义Core Graphics绘图代码即可轻松完成此操作。 让我们深入研究一下实现的外观。 设置东西 我们将从将复制器层设置为UIView层的子层开始,以便在屏幕上进行渲染(本文中的示例代码将针对iOS / tvOS,但其工作方式几乎完全相同苹果系统): let replicatorLayer = CAReplicatorLayer() replicatorLayer.frame.size = view.frame.size replicatorLayer.masksToBounds = true view.layer.addSublayer(replicatorLayer) 我们 masksToBounds 上面 […]

使用适用于macOS App的App Sandbox

沙盒是保持应用程序数据访问可控的想法。 每个硬币都有两个面。 让我们看看两者。 没有沙箱 传统上,Mac Apps没有沙盒,开发人员可以完全访问计算机中的所有资源。 例如,一个人可以从任何位置存储和读取文件。 通常,默认情况下会将相同类型的文件放在一起。 以下是一些常见的路径: #文件 〜/图书馆/应用程序支持/com.abc.MyApp/ #NSUserDefaults 〜/ Library / Preferences / com.abc.MyApp.plist #缓存 〜/图书馆/应用程序支持/com.pinterest.PINDiskCache.mycache/ 在此示例中,“ com.abc.MyApp”是我的应用程序的捆绑标识符。 我正在使用PINCache来存储缓存,并将其持久化到磁盘空间。 您会注意到PINCache所存储的文件与应用程序文档处于同一级别。 非沙盒的问题 我知道有些开发人员喜欢完全可配置的环境。 但是,此方法存在一些问题: 1)骇客 由于应用程序可以访问整个系统,因此如果它被恶意代码注入,则系统可能会被黑客入侵 2) 杂乱的文件存储 由于文档,设置,缓存存储在不同的位置,因此花费了更多的开发工作。 例如,如果我想清理文件以重置应用程序,则应前往各个位置进行清理。 使用应用沙箱 随着iOS的发布,这一概念成为一种规范。 每个iOS应用程序都应带有自己的沙箱。 因此,开发人员应要求特定的权限才能获得系统中的其他资源。 为了通过Mac App Store分发应用程序,开发人员必须打开App Sandbox授权。 例如,我正在构建一个使用CloudKit同步数据的应用程序。 我将在“传入和传出网络连接”两个框中打勾。 如果要将检索到的CloudKit记录保存到Documents文件夹中的文件中,则应为“ User Selected File”选择“ Read / Write access”。 在沙盒模式下,所有文件都存储在一个容器中。 例如,上一个示例中的位置将变为: 〜/图书馆/应用程序支持/容器/com.abc.MyApp/Data/图书馆/应用程序支持/ 沙盒问题 […]

👀调试您的Quick Look插件

当您想将Quick Look插件捆绑在您的应用程序中,并且对该应用程序有一些依赖性时。 调试此Quick Look插件可能很麻烦。 怎么样? 现在最简单的测试方法是什么? 这有点愚蠢,但这始终对我有效100%。 构建您的应用程序目标(捆绑了Quick Look插件)。 在Xcode项目文件夹结构中,应该打开“ 产品”组,然后右键单击.app文件,以便在Finder中显示它。 现在,将.app文件复制到/Applications文件夹。 该应用程序已经存在时,只需将其覆盖即可。 打开新复制的应用程序。 现在,强制重新加载“快速查看生成器”列表。 qlmanage -r运行qlmanage -r 。 选择要使用插件预览的文件,然后点按空格键。 别客气! 🙌 更多技巧 当您想要显示一些日志记录时,您可以做的最好的事情就是在代码中使用NSLog 。 专业提示 :使用表情符号可以更好地查看日志。 运行qlmanage -m | grep gpx qlmanage -m | grep gpx命令可以查看链接到gpx扩展名的所有Quick Look插件。 这样,您可以确定使用的插件正确。 可能使用了错误的Quick Look插件,最简单的方法是删除错误的插件,然后再次运行qlmanage -r 。 可能并非总是有效的另一件事是重新加载缩略图缓存 。 在运行qlmanage -r cache命令之后,您将需要重新启动Finder以重置图标。

Xcode,终极的调试和破解工具

调试一直是我在计算机上的一部分经验,甚至在学习编码之前也是如此。 但是,展开二进制文件和修改程序行为的满意度从未如此轻松。 通常,调试包括从十六进制编辑器来回切换到调试器,应用程序,终端,计算器等,更不用说程序退出了,您必须遵循很长的配方,直到调试会话突然结束。 不好… 但是几个月前,以破解适用于macOS的Twitter为借口,我决定最小化此问题并优化此流程。 这就是Xcode进行救援的时候。 设定 首先,我们需要创建一个以Cocoa框架为目标的Xcode项目。 调试 我们将使用嵌入式lldb控制台进行动态调试。 想象一下,适用于macOS的Twitter是我们要调试的应用程序。 通常,我们需要运行lldb并从调试器启动该进程,相反,每次我们单击Run按钮时,Xcode都会为您执行此操作。 为此,请在我们框架方案的“运行”配置中选择Twitter应用程序作为可执行文件。 这样,将编译我们的框架,并在进程附加了lldb的情况下启动Twitter应用程序。 注入代码 到目前为止,我们所做的可能已经足够了,我们可以轻松地创建针对应用程序的调试会话,还可以利用Xcode提供的自动完成功能。 但是注入代码呢? 代码注入通常涉及编译动态库并修补二进制文件,以便它链接到我们的库。 尽管如此,调试注入的代码仍然是一项繁琐的任务,并且尽管我们始终可以使部分代码自动化,但该过程缓慢而重复。 现在,借助Xcode灵活的配置方案和一些魔术,我们可以轻松修补正在检查的应用程序的运行时,而无需修改其二进制文件(双赢)。 例如,让我们注入一些代码,记录每次对viewDidLoad调用。 Cocoa Framework项目模板创建一个名为nameOfTheProject.h的头文件。 我们将创建其各自的.m文件,并将混乱的逻辑放入其中。 照原样,Xcode将编译我们的框架,并在调试器附加到其过程的情况下启动Twitter应用程序。 链接这两者就像使用环境变量运行过程一样容易,该环境变量迫使位于给定路径上的库加载。 这个环境变量是DYLD_INSERT_LIBRARIES ,是我们库${TARGET_BUILD_DIR}/${TARGET_NAME}.framework/Versions/A/${TARGET_NAME}的路径。 在方案的“运行”配置的“ 参数”选项卡中设置环境变量后,单击“运行”按钮。 我们的代码应该已经正确注入到Twitter流程中。 还不够好? Xcode提供的所有其他调试工具也都可用,从视图层次结构到内存图调试器。 加起来 无论您是否进行逆向工程,我都建议您尝试一下,也可以使用TwitterX来获得有关此设置功能的更多信息。

无需虚拟化即可在Mac OS上重现和包含CI

最近,我一直在研究Mac OS(特别是iOS)的持续集成的状态。 我想了解人们在Mac OS上如何进行CI,以及常见的最佳做法是什么。 不幸的是,对于我来说,没有什么惊喜,因为Mac OS的构建自动化状态非常糟糕。 在这篇博客文章中,我将总结一下我的发现,并描述我提出的实验性解决方案。 首先,我们需要弄清楚我们在寻找什么版本。 良好的CI设置通常旨在确保以下几点: 灵活可复制的环境 。 应该可以指定执行构建的环境。 不同类型的构建可能需要不同版本的工具和框架,CI应当保证某种类型的构建将始终在同一环境中执行。 隔离建筑物 。 顺序或并行构建不能相互干扰执行。 对于每个新构建,应从先前构建的工件中清除环境。 表现 。 与在开发人员的本地计算机上构建的CI相比,CI构建的性能应具有最小的影响。 常见的CI系统使用一种模式,在该模式下,执行构建AKA工作的计算机会安装一个称为CI代理的程序,该程序会不断拉动主服务器进行工作。 早在20年前,代理只是运行shell脚本,开发人员的工作是确保这些脚本编写正确并提供上述保证。 但是根本不可能做出脚本,最重要的是要保持这样的保证。 从那以后,不幸的是,并没有发生很多变化 。 该体系结构中最显着的变化是使用虚拟机作为CI工作者的转变,以拥有这种灵活且可复制的环境。 除此之外,设置CI系统甚至使用完全托管的解决方案变得更加容易。 这是Uber工程师的精彩演讲,内容涉及他们从一台物理Mac Mini到数千台虚拟机的旅程(始于13:20,Medium不支持时间标签)。 这篇演讲基本上展示了上述事物的整体演变。 这是一个./chamber.sh ./script/ci.sh脚本,可以像./chamber.sh ./script/ci.sh一样使用它来在./chamber.sh ./script/ci.sh中运行现有的CI脚本。 该脚本已经在多个项目上进行了测试,但仍然是WIP。 我强烈建议您尝试一下并报告任何问题。 不过,请阅读它,其中包含有关其功能以及如何调试/配置功能的详细注释。 运行虚拟机仍然很困难。 如果不是苹果公司,那么也许像Veertu这样具有现代Anka虚拟化技术的公司将为Mac OS的自动化带来类似Docker的感觉。 在此之前,人们将寻找解决方法和黑客手段。 本文中介绍的Chamber概念只是一个有趣的想法,似乎适用于一些中小型项目。 但这是实验性的 。 仍然存在一些问题: 测试各种项目。 使xcodebuild在nix-shell 。 我无法使其正常运行,因此使用nix-shell来计算用于在沙箱中运行脚本的PATH 。 目前尚不清楚如何支持多个Xcode版本。 应该是Nix包装吗? 它应该是一个单独的工具吗? 我希望这是一个有趣的阅读。 如有任何疑问,请随时在Twitter上对我进行ping操作。 […]

在macOS上创建菜单栏应用

菜单栏应用程序是指位于macOS菜单栏(即状态栏)上的应用程序。 它提供对关键功能的即时访问,以及在登录会话期间始终可访问的功能。 创建状态栏项目 我们使用NSStatusBar.system().statusItem(withLength: -1)从系统请求状态栏项目。 请注意,如果状态栏已经包含许多其他状态栏项目,则可能会失败(返回nil)。 成功获取状态栏项后,您将获得一个NSButton作为要放在菜单栏上的UI元素。 您需要通过提供图像以及设置按钮的目标和操作来对其进行配置。 所有这些都应在应用启动期间完成,因此在应用委托中,我们具有: 请注意,必须使用强引用来保留系统返回的状态栏项目对象。 否则,它将在以后释放,并且菜单栏上将不会显示任何内容。 显示菜单 要显示菜单,您需要提供放置菜单对象的参考点,在这种情况下为状态栏按钮。 然后,我们创建一个鼠标事件,并使用popUpContextMenu(_:with:for:)出菜单。 信息清单设置 如果您的应用程序仅是菜单栏(类似于Dropbox macOS客户端),则需要在Info.plist中设置Application is agent = YES ,以便省略Dock的应用程序图标和左上角的应用程序菜单。 下载样例项目

本地化macOS应用程序。

https://appfillip.com/important-tips-for-app-store-localization-help-to-increase-app-ranking-globally/ 世界上有太多语言(超过6500种)。 但是,地球上的大多数人口(约60–70%)仅使用其中的50–60。 对我们有利,我们不需要将我们的应用程序转换为所有6500! 实际上,并非每个应用程序都需要本地化,它取决于客户群(大多数用户使用哪种语言)以及该应用程序的受欢迎程度。 此外,如果他们在理解内容时遇到困难,可以将本地化视为可以鼓励用户使用该应用程序的功能。 我一直在寻找一个有助于发展的项目,却发现了一个名为IINA的项目。 伊娜 IINA是适用于macOS的现代视频播放器。 它具有本机播放器(快速时间)不具备的许多功能,例如制作播放列表,添加字幕,缩略图预览等。 同时,它具有漂亮的用户界面,与“快速时间”非常相似。 IINA仍在发展中。 我打开了一个问题页面,找到了一些尚待解决的错误,然后发现要求对多种语言进行本地化。 我在应用程序本地化方面没有任何经验,因此认为这是学习如何进行的好机会。 在不同的网站上有很多关于如何本地化iOS / macOS应用程序的教程。 他们非常简单,并通过分步说明详细解释了所有内容。 本质上,本地化是通过将强类型文本( UILabel , UIView等中的所有文本)移动到单独的文件中来完成的。 所有本地化的字符串都存储在扩展名为.string文件中。 内容很简单,由键值对组成。 在我刚开始的时候,INIA已经针对多种语言进行了本地化(对于某些语言仍然不完全适用)。 但是,如果您正在处理的项目没有任何本地化,则可以从添加一种新语言开始:在项目导航器的“信息”选项卡中有一个“本地化”部分。 只需单击+按钮即可添加新语言。 如果要将本地化添加到新项目中,我建议使用本教程。 由于已经为该项目设置了本地化,所以我决定检查俄罗斯和乌克兰的本地化。 要使用特定的本地化环境运行应用程序(更改应用程序语言),您需要在xCode中编辑方案,或者更改系统语言(老实说是个坏主意)。 导航到“产品”->“方案”->“编辑方案”,或单击活动方案(位于旁边的左上角 按住alt键的同时点击“ stop按钮)。 在这里,您可以选择一种语言,该语言将是使用xCode运行该应用程序时的默认语言。 因此,我以两种语言运行了IINA,发现它并没有完全本地化为乌克兰语。 我首先过滤所有文件以仅获取用于乌克兰语本地化的.string文件(例如uk.lproj / PrefGeneralViewController.strings或iina / uk.lproj / PrefCodecViewController.strings)。 这些文件与Localize.string文件不同,因为它们没有键的明确名称,而是包含UI元素的ObjectID (例如“ hWC-KJ-FuC.title”)。 因此,在Localize.string文件中添加缺少的翻译很容易。 为其他文件添加翻译要困难一些,因为几次我遇到缺少的ObjectID ,这意味着具有这些ID的对象将无法翻译。 为了修复它,我为相同的ViewController打开了本地化文件,并从那里复制了缺少的ObjectID 。 现在,我唯一需要做的就是翻译它们。 完成这些操作后,我再次运行该应用程序,并注意到整个菜单选项卡仍未翻译。 那是因为我只向缺少的.string文件添加了缺少的翻译。 很明显,该菜单选项卡的翻译文件不存在。 要为该视图添加本地化文件,我为PrefUtilsViewController打开了一个xib文件,并在“检查器”菜单中选中了缺少语言的复选框。 […]

如何从NSTokenField删除令牌? [迅速4.2]

在过去的几天中,我花费了大量时间正确地实现了具有某些拖放功能的标准NSTokenField。 知道了这一点,我们可以检查objectValue对象的类型是否等于我们期望的代表对象的类型。 如果确实如此,我们知道我们需要删除整个令牌。 然后,我们通过将stringValue拆分为一个数组并将其与objectValue中的对象数进行比较来检查对象数。 如果让 anyObjectsArray = control.objectValue 作为 ? [ 任何 ], 让 tokenFieldObjects = anyObjectsArray 为 ? [标签] { //现在,我们继续删除令牌… 让 stringObjectComponents = control.stringValue.split(separator:“,”) //检查令牌数是否等于tokenField的stringValue中的对象 如果 tokenFieldObjects.count == stringObjectComponents.count { //接下来,找出要删除的对象 } } 我们确信,我们必须删除令牌。 但是,在此阶段,我们不知道必须删除哪个令牌或多少个令牌。 幸运的是,找到它相对容易(当您知道时)。 如何找出要删除的令牌? 实例方法control(_:textView:doCommandBy :)还提供了NSTextView,如果您想找出光标在令牌字段中的位置,该类非常方便。 NSTextView有一个名为rangeForUserTextChange的方法,该方法返回一个NSRange,该NSRange由位置和长度组成。 var textChangeLocation = textView.rangeForUserTextChange.location 让 textChangeLength = textView.rangeForUserTextChange.length 我们暂时忽略长度 (稍后再讨论),并专注于location 。 我们只需要检查位置是否少于对象数。 如果是的话,我们需要将其减少1以获取对象在数组中的索引,因为数组的索引为零,而NSRange从1开始。 […]

Swift 4中的泛型–开发人员思想–中

以良好的格式阅读此文章,并在 Swift Post 上突出显示语法 。 作为Swift中最强大的功能之一,泛型可能会很棘手。 很多人在理解和使用它们时遇到麻烦,尤其是应用程序开发人员。 泛型最适合于库,框架和SDK。 在本文中,我将尝试与其他教程不同的方法。 我们将开设一家餐厅,并获得SwiftyCity市议会的许可。 为了诚实起见,我将尝试将事物归为四个主题。 泛型函数和泛型类型 关联类型的协议 通用条款 通用下标 开始了! 泛型函数和泛型类型 开一家斯威夫特餐厅 让我们建立一个新餐厅。 设置过程中,我们不仅会关注我们餐厅的建设,还将研究法律部分,并获得市议会的许可。 此外,我们将专注于我们自己的业务以使其运作并实现盈利。 首先,公司在理事会中的样子如何? 公司应具有一些基本功能。 protocol Company { func buy(product: Product, money: Money) func sell(product: Product.Type, money: Money) -> Product? } buy功能将产品添加到库存中,并从公司的金库中取出资金。 另一方面, sell函数获取产品类型,创建/查找产品并返回以换取金钱。 泛型函数 在此协议中, 实际类型的 Product听起来不正确。 不可能将每个实际产品都放入Product类型。 每个产品具有不同的功能,特性等。 在这类函数中使用实际类型确实是个坏主意。 让我们回到理事会。 在世界各地,无论每个公司做什么,都需要具有购买和出售的功能。 因此,理事会必须找到针对这两个职能的通用解决方案,并使它们适用于每个公司。 他们可以使用Generics改进这些功能: protocol Company […]

在Swift中包装C库(第1部分)

这篇文章是在Swift中包装C库的多部分指南中的第一篇。 第1部分将逐步完成构建Swift项目的过程,该项目可以使用Swift Package Manager(SPM)与C库libgraphqlparser进行交互。 其他部分将介绍如何将C接口包装为使用起来更自然的Swift API。 在探索特定库的示例时,此处描述的相同技术可以应用于大多数其他C库。 自2016年以来,在Shopify,我们一直在移动应用程序中使用GraphQL。GraphQL提供的优于典型REST API的优势之一是它具有定义明确的架构。 可以利用GraphQL模式编写代表各种查询和变异的网络响应的强类型Swift代码。 这段代码编写起来很繁琐且容易出错,因此我们决定构建一个可自动生成代表您的GraphQL查询和变异的Swift模型的工具。 构建这样的工具的首要挑战之一是找到一种方法来解析GraphQL语法中定义的查询和变异,并将其转换为可由Swift代码理解的抽象语法树。 构建语法分析器并非易事。 幸运的是,GraphQL组织已经发布了一个用C ++编写的开源解析器。 Swift无法直接与C ++代码互操作,但是libgraphqlparser项目提供了一个纯C API,只需做一点工作,就可以在Swift中使用它。 libgraphqlparser库的标头中定义了一些功能,可以完全满足我们的需求。 从libgraphqlparser的C头文件 第一个函数将采用GraphQL查询字符串,并将其转换为查询的AST表示形式。 对于本教程,我们将仅使用graphql_ast_to_json(ast)将AST转换为JSON并打印结果。 设置基本软件包 SPM要求定义一个包装系统库的Swift软件包,但是由于没有Swift代码,因此无法直接构建该软件包。 为了验证该库可以导入到Swift代码中,基本包必须将包装器包作为依赖项导入。 让我们构建该基本软件包。 $ mkdir GraphQLParser $ cd GraphQLParser GraphQLParser $ swift软件包init –type可执行文件 安装库 安装libgraphqlparser可以通过从源代码构建或使用以下命令通过自制程序来完成。 $ brew安装libgraphqlparser 完成初始项目设置后,我们需要将代码公开给Swift。 由于这是针对macOS命令行应用程序的,因此我们可以使用Swift Package Manager(SPM)。 为了向Swift代码公开系统库,我们需要告诉编译器在哪里可以找到我们要使用的代码。 通常,这包括将动态库的位置和任何必需的头文件的位置通知编译器。 在这种情况下,动态库可能已安装到/usr/local/lib/libgraphqlparser.dylib ,并且头文件将已写入/usr/local/include/graphqlparser 。 SPM通过要求用户定义一个包装系统库的程序包并为该库提供模块映射来解决此问题。 约定是在这些程序包之前使用大写的“ C”作为前缀,因此该程序包将被称为“ Clibgraphqlparser”。 在与GraphQLParser文件夹相邻的目录中初始化此软件包。 GraphQLParser […]