Tag: 迅捷

着色器的ARKit + SceneKit简介

之前我谈到过如何在SceneKit中创建自己的自定义几何,结果您可以使用它们进行某些操作,例如偏斜或动画。 请参阅此处的第2部分的第1部分: ARKit + SceneKit几何图形教程(第1部分) 在SceneKit中创建自己的几何形状,无需离开即可拥有各种场景和动画。 medium.com 在该系列的第二篇文章的结尾处,我展示了如何使用看起来像字符串的标志来设置标志的动画,这是在SceneKit中向几何体或材质添加着色器的一种方法。 在许多情况下,比起我所展示的其他示例,诸如我上一篇文章底部所示的着色器是使几何图形动画化的更好选择。 主要原因是这些功能由SceneKit接受,编译为Metal,然后在设备的GPU上运行,该GPU比要在其他地方运行的CPU强大得多。 这主要是因为GPU是专门为处理图形而构建的(因此,其名称为Gphphics Processing U nit),所以我们希望尽可能多地使用它! 我想在本文中提到的应用着色器的方法主要是通过使用SCNGeometry对象的shaderModifiers属性。 首先,请看这里: SCNShadable – SceneKit | Apple开发人员文档 着色器修改器是Metal着色器语言或OpenGL着色器语言(GLSL)中的源代码片段,可用于… developer.apple.com 作为一个简单的示例,请看以下小片段: 如果(_geometry.position.y> 0){ _geometry.position.x + = 0.5; } 假设我们将其应用于尺寸为SCNBox类型的几何。 _geometry代表顶点,正如我之前的文章中所述,其位置当然是其在3D空间[x, y, z] 。 在此代码段中,我们获取位置在立方体中心上方的所有顶点,并将其向x方向正方向的侧面推0.5m。 我们可以迅速使它变得更有趣,而无需考虑太多,例如: 如果(_geometry.position.y> 0){ _geometry.position.x + = 0.5 * sin(u_time); } u_time是SceneKit开始渲染此着色器以来的时间(以秒为单位)。 因此,这将在正弦缓和之后以平滑的方式将立方体的局部x轴上+0.5的这4个点动画化为-0.5。 最后,我将演示的多维数据集示例是使用单位圆方程将前四个点从沿直线移动更改为沿圆移动。 单击此处以查看Desmos的快速图表,以防万一您不确定该如何组合。 如果(_geometry.position.y> 0.0){ _geometry.position.xz + […]

ARKit Pods — SCNPath

许多ARKit开发人员正在使用SceneKit作为其入门框架,这是一个很好的选择,主要是因为它可以访问iOS开发人员用于所有其他应用程序的所有本机功能。 使用SceneKit,您可以访问许多默认的几何类型,SCNBox,SCNSphere,SCNPyramid甚至SCNShape。 这些是很好的构建基块,但是当您要构建一些稍有不同的东西时,则必须更深入地自己构建几何。 我认为这是所有ARKit开发人员至少应该意识到的事情,这就是为什么我在之前的一些Medium帖子中展示了示例的原因,从我的第一个帖子开始: ARKit + SceneKit几何图形教程(第1部分) 在SceneKit中创建自己的几何形状,无需离开即可拥有各种场景和动画。 medium.com 大多数iOS开发人员不一定都具有3D几何或图形方面的背景知识,而且我认为他们不需要为了构建出色的东西而拥有背景知识! 因此,我最新的开放源代码框架用于创建基本为三角形的路径,这些路径可以围绕点集(SCNVector3的数组),如下面的GIF所示。 SCNGeomtry.path(path: positionArray) SCNPathNode(path: positionArray) 要创建一条从世界原点(或下面的1m)开始,向前然后向右的路径,您需要做的就是创建这样的类: 让pathNode = SCNPathNode(path:[ SCNVector3(0,-1,0), SCNVector3(0,-1,-1), SCNVector3(1,-1,-1) ]) 默认情况下,该路径的宽度为0.5m,它具有可以更改的其他属性,有关所有信息,请参见GitHub存储库: maxxfrazer / ARKit-SCNPath 仅使用点代表路径的中心为增强现实环境创建路径。 … github.com 好的,让我们来制作我在推文中展示的应用程序: 接下来,我们要添加点击手势。 当且仅当正方形未初始化(附加到屏幕)时,我才选择仅使用轻击手势来获取FocusSquare的当前位置,但是如果您希望执行的话,可以执行一次hitTest,用手指代替屏幕。 这些函数在ViewController中或在其扩展中。 var hitPoints = [SCNVector3]() func setupGestures(){ let tapGesture = UITapGestureRecognizer(target: self ,action: #selector (handleTap( _ :))) tapGesture.delegate = 自 自 .view.addGestureRecognizer(tapGesture) […]

用于iOS / macOS开发的拖动手势识别器(具有初始位置)

众所周知,在计算机上使用鼠标时,按下鼠标按钮会发生拖动操作,然后移动设备,最后释放按钮。 这是连续且精确的操作。 在iOS中,我们使用了(不太精确的)触摸,但是有时我们确实希望在开始,移动以及最终结束触摸时对拖动操作做出类似的反应。 UIPanGestureRecognizer本身可以很好地识别此类拖动操作,只是当其达到开始状态时,用户已经稍微移动了触摸,并且我们不再知道初始位置-没有locationInViewWhenTouchesBegan属性-而我们需要它来准确计算坐标增量以响应拖动操作。 (以一种预期的方式,因为我们不希望在第一次触摸开始时立即开始平移操作,因为我们正在等待用户是否真正想平移或执行其他操作。) 更新:虽然这很烦人,但它也是NSPanGestureRecognizer在macOS Cocoa开发中与鼠标事件一起工作的方式,只是很难看到那里的行为。 要解决该问题,您需要开始用鼠标从元素可拖动区域的限制向该元素外部的空间拖动。 为了解决这个问题(至少在今天),我们需要自己专门化识别器类,在实际开始触摸时读取并保留视图中的位置,并改用专门化。 请注意下面的内容,我们为此目的如何定义一个初始位置视图属性。 (不过,如果要在框架之外使用它们,则需要将类和属性公开 。) 导入UIKit.UIGestureRecognizerSubclass class DraggingPanGestureRecognizer:UIPanGestureRecognizer { var locationInViewWhenDraggingStarted = CGPoint.zero 覆盖func touchesBegan(_ touches:设置,事件:UIEvent){ super.touchesBegan(touches,with:event) locationInViewWhenDragStarted =位置(在:视图中) } } 最后,这是显示如何使用新服务的客户端代码: @IBAction函数泛(_识别器:DraggingPanGestureRecognizer){ 切换识别器状态{ 案例。开始: 让点= ogniser.locationInViewWhenDraggingStarted beginDragging ( 位于 :点) 情况已更改: 让点= identifier.location(在:ognitor.view) ContinueDragging ( 到 :point) … } } 和/或,如果您需要类似的实现来进行macOS开发(例如),即从NSPanGestureRecognizer继承,这里我们再做一次(但请注意,客户端代码与上面的代码完全相同,因此在下面不再赘述): 导入AppKit类DraggingPanGestureRecognizer: NSPanGestureRecognizer { var locationInViewWhenDraggingStarted = […]

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

迅捷的懒惰属性

什么是惰性关键字? lazy关键字使您可以延迟存储属性的初始化,直到首次访问它为止。 您只能在结构或类中使用惰性。 //如果尝试在结构或类之外的属性上使用惰性,编译器会吐出错误: 惰性仅对结构或类的成员有效 您为什么要使用它? 有时无法初始化存储的属性,因为其值取决于尚未初始化的代码的其他部分,或者应在首次访问该属性之前不进行初始化,以便节省内存并提高速度甚至防止您的应用崩溃(例如从数据库中获取和/或排序数据) 在类中使用惰性属性 要看的东西: 惰性关键字 :使用此关键字延迟变量的初始化 使用闭包 :由于该属性的目的是延迟某些代码的执行,并且计算后的属性不能进行延迟初始化,因此我们使用闭包来初始化对象 ()在闭包末尾:这是闭包自身执行的方式。 如果不使用(),则只能将闭包分配给该变量而不执行它 初始化importFromDatabase类时,不会初始化lazy属性 当您调用lazy属性时,闭包将被执行 我在学习有关此问题时遇到了一些问题: 可以将lazy与常量一起使用吗? 不,您不能这样做,因为惰性属性的值仅在初始化后才计算,而常量则需要一个值 它对计算属性起作用吗? 不可以,因为总是重新计算一个计算属性,而惰性属性在初始化时仅计算一次。 为什么使用闭包? 由于该属性旨在执行代码,因此闭包是必经之路 为什么要使用惰性属性? 初始化对象时延迟执行代码

RxSwift世界中的ViewModel

牢记这一点,我想将ViewModel视为一个“黑匣子”,它接受一些UI触发器(按钮点击,表视图选择,文本编辑事件等),其他依赖项(NeworkService,DataBaseService,LocationService)并应用一些Rx运算符(确定业务逻辑)。 然后,从视图模型中,您可以获取转换后的可观察对象,并将其绑定回您的UI以应用您的业务逻辑。 作为示例,我想展示如何实现可搜索数据列表,并在带有搜索栏的表格视图中显示它 假设所有实现模型的人员都要做的就是创建ViewModel和ViewController 因此,让我们定义UI触发器: 搜索触发器(用户可以键入以搜索列表中的数据) 滚动触发器(用户可以滚动以从列表中提取新数据) 现在我们可以定义ViewModel接口 现在让我们定义要应用于初始触发器的转换 从搜索查询转换为请求 防止触发空查询请求 防止用户每次输入新字符时发出火灾指令 取消先前的请求以支持新请求 每次用户滚动到滚动视图的底部边缘时都命中请求 用新数据附加先前状态(数组) 实施转换: 过滤空字符串,请记住我们不要触发空查询的请求 防止用户每次输入新字符时触发请求,仅在暂停0.3秒时才触发 将搜索查询转换为请求并取消之前的查询 将英雄转变为 虚拟HeroCellData (例如标题,图像网址) 将HeroCellData数组转换为HeroCellSection (这需要将其绑定到UITableView) 触发下一页请求 现在让我们将转换后的Observable绑定回UI 创建ViewModel 将主表项绑定到UITableView 将搜索项绑定到UISearchController的tableView 摘要: 我们的视图模型是“纯”的,它是不可变的,我们甚至不需要在ViewController中对其进行引用( disposeBag使订阅保持活动状态) 所有逻辑封装在一个地方 可以使用RxTests轻松测试它。 在下一篇文章中🙂 进一步阅读: Rx简介 RxSwift书 Rx.io RxDataSources 原始码 快乐的RxSwift编码! 🙂

隐藏窗口,而不是单击关闭按钮时将其关闭

开发单个窗口式,非基于文档的Cocoa应用程序时出现问题。 也就是说,当用户单击窗口上的关闭按钮时,它将关闭,但是无法将其恢复。 为了解决这个问题,我们应该“隐藏”窗口,而不是在用户尝试关闭窗口时将其关闭。 Xcode模板的最新版本针对单个窗口应用程序使用情节提要和窗口控制器,除其窗口控制器外,无法将窗口的委托连接到其他对象。 因此,这给了我们两个选择:1.子类化NSWindowController; 2.用代码将窗口的委托连接到目标对象,例如一个NSViewController对象。 解决方案1.子类化NSWindowController 创建一个新的NSWindowController子类,并在头文件中添加协议一致性(如果使用Swift,则不这样): 目标C: @interface VCWindowController:NSWindowController @结束 在实现文件中: #import“ VCWindowController.h” @interface VCWindowController() @结束 @implementation VCWindowController -(void)windowDidLoad { [super windowDidLoad]; //实现此方法以处理从其nib文件加载窗口控制器的窗口之后的所有初始化。 } -(BOOL)windowShouldClose:(id)sender { [NSApp hide:nil]; 返回否; } @结束 迅速: 进口可可 VCWindowController类:NSWindowController,NSWindowDelegate { 是否需要初始化?(编码器:NSCoder){ super.init(编码器:编码器) } func windowShouldClose(sender:AnyObject)-> Bool { NSApp.hide(无) 返回假 } } 创建类后,现在可以将代码连接到窗口。 一种。 将窗口控制器设置为自定义NSWindowController子类: b。 右键单击Window,将“代理”连接到Window Controller对象: 现在构建并运行您的应用程序,单击“关闭”按钮将仅隐藏您的应用程序,而不是关闭它。 […]

如何在Swift中创建私有CocoaPods

CocoaPods是Swift和Objective-C Cocoa项目的依赖项管理器。 CocoaPods可以帮助我们优雅地扩展项目。 可以使用CocoaPod开发出色的库,只需运行一个命令即可轻松将其集成到项目中。 在这里,我们将讨论如何创建我们自己的CocoaPod(恰好是一个私人吊舱)。 先决条件: Xcode 9.3和Swift 4.1 安装在机器上的CocoaPods 让我们逐步开始创建我们的私人吊舱。 步骤1:建立广告连播 要创建和验证Pod,我们将使用pod lib命令。 因此,让我们开发一个String的简单扩展(还有更多),这将使几个字符串函数变得很方便。 让我们将此吊舱命名为HandyExtensions 使用以下命令创建一个Pod,该Pod将下载模板并简化我们的工作。 pod lib创建HandyExtensions 运行以上命令将提示您回答一些问题,例如平台,编程语言,所需的演示应用程序等。 Pod模板将被下载。 导航到Example文件夹,然后打开HandyExtensions.xcworkspace并使用所有相关信息编辑podspec文件(在Podspec Metadata文件夹下)。 摘要,描述,源和依赖项(如果适用)是您要记住的几件事。 默认情况下,源将显示为 s.source = {:git =>’https://github.com/sagardesai16/HandyExtensions.git’,:tag => s.version.to_s} 我们将必须将源更新为我们的私人仓库网址。 我更喜欢使用BitBucket。 s.source = {:git =>’https://abc@bitbucket.org/abc/handyextensions.git’,:tag => s.version.to_s} 重要提示: 确保根据库的要求更新deploy_target。 如果您打算在Pod库中使用资产(png),则必须取消注释resource_bundles s.resource_bundles = {‘HandyExtensions’=> [‘HandyExtensions / Assets / *。png’]} 步骤2:验证podspec 由于我们已更新podspec文件中的所有相关信息。 让我们验证podspec文件并检查一切是否正常。 打开终端并转到您的podspec文件所在的目录,然后执行以下命令: 豆荚皮棉 […]

为XCode项目安装Cocoa Pods

在这篇文章中,我将展示如何为Xcode项目安装Cocoa Pods 。 Cocoa Pods是Swift和Objective-C Cocoa项目的依赖项经理 。 安装 在安装XCode时,您还将获得gem ( RubyGems , Ruby包管理器 )。 此命令允许您安装Xcode的Cocoa Pods,而无需任何额外配置。 执行以下命令 sudo gem install cocoapods 在获取依赖包列表之后,您可以使用可可豆荚调用命令pod 。 但是,每个命令都必须在 Xcode项目的 根文件夹中执行 : pod init :在Xcode项目文件夹中创建一个空的初始PodFile 。 pod install :安装PodFile定义的PodFile 使用Cocoa Pods命令行 照常创建Xcode应用程序。 然后,在安装Cocoa Pods之后,您可以创建执行pod init的初始PodFile 在应用程序的根文件夹中。 之后,您将获得一个PodFile像这样: #取消注释下一行以定义项目的全局平台 #platform:ios,’9.0′ 定位 “我的应用名称” #如果您不使用Swift并且不想使用动态框架,请注释下一行 use_frameworks ! #我的应用名称荚 结束 如果您想添加一些Pod,例如Google Maps iOS SDK组件,则可以将其添加到target部分: […]

Swift类:基础知识

描述:类与结构非常相似,但是在决定使用哪种结构时要知道有重要的区别。 使它们与众不同的是,类可以从其他类继承,具有反初始化器并允许对一个类实例的多个引用。 结构不能做这些事情。 声明一个类与使用结构完全一样,但是从关键字class开始。 属性:类,因为结构可以具有存储的属性或计算的属性。 存储的属性可以具有观察者,计算的属性可以具有setter和getter(请参见Swift计算属性)。 如果为所有属性提供默认值,则似乎不需要使用初始化程序。 在这种情况下,我们正是这样做的。 初始化程序即将推出,然后我们将删除默认值。 方法:与类关联的函数是否与结构相同。 但是,与结构不同,如果要更新类中的属性值,则无需为方法使用关键字mutating。 初始化器:类没有默认的初始化器(结构具有默认的初始化器),这意味着您需要创建自己的初始化器,以便所有属性都具有值。 如下所示,即使已分配了默认值,初始化器也将为类的属性分配值。 继承定义了一种关系,在该关系中可以根据超类定义子类。 继承是类和结构之间的主要区别。 子类继承了超类的所有功能(属性,方法,下标),并可以通过点语法对其进行访问。 如下所示,我创建了子类InheritanceExample ,该子类继承了BudgetTest的所有功能。 无需在子类中设置任何功能,就可以使用超类的初始化程序创建InheritanceExample的实例, 我可以通过点语法访问超类功能。 子类也可以放置其自身的功能 在下面的代码中, printDescription和ownFeature特定于此类。 您还可以注意到使用了super.init,它调用了超类的初始化程序。 初始化子类的过程如下:首先初始化子类的属性,然后调用超类的初始化程序以将值设置为其自身的属性。 覆盖方法,属性和下标:我们使用关键字override来指示子类将根据您要更改的内容来更改属性,方法或下标。 Override要求编译器验证超类是否具有可以被覆盖的匹配声明,如果不存在匹配则抛出错误。 如果在某些示例中遇到了关键字super ,请知道它用于访问超类的功能。 请参阅以下示例: 在上面的代码中,我重写了一个方法,并通过使用super关键字调用了该方法的超类实现。 这使我可以扩展该超类的方法,因为可以为其添加新功能。 防止超越? 在变量,方法之前使用关键字final