如果您是 Swift 的新手,您可能会发现 我关于 3D Touch 的上一篇文章 有点令人生畏。这是一个 小得多的项目 ,通过强制、查看、弹出和预览操作可能更容易遵循以启动和运行。
ChromaTouch 是一种基于 HSL 的颜色选择器,用户可以使用三个水平滑块或通过触摸颜色样本来设置颜色,其中水平移动设置色调,垂直移动设置饱和度,触摸力度设置颜色的亮度。当用户在样本上移动手指时,滑块会更新以反映新的 HSL 值。
通过用力触摸滑块,用户会看到一个小预览,显示其颜色的 RGB 十六进制值:
通过向上滑动,他们可以将颜色设置为三种预设之一:
通过深压,他们会看到他们颜色的全屏预览,点击即可关闭:
让我们看看 3D Touch 代码的每个部分,看看所有内容是如何实现的。
以力定轻
我的
Swatch
类负责处理用户在三个维度上的触摸,并填充一个包含色调、饱和度和亮度三个值的元组。
touchesMoved()
中返回的每个触摸都在其
touchLocation
属性中包含二维空间数据,并在其
force
属性中包含用户施加的力的大小。
我们可以通过将位置除以
Swatch
视图的边界并将力除以
maximumPossibleForce
来将这些值标准化为介于 0 和 1 之间。使用这些标准化值,我们可以构建一个对象来表示我们所需颜色的三个属性:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
如果您查看我的代码,您会注意到我保留了一个变量来保存之前的触摸力并将其与当前的触摸力进行比较。这让我可以忽略当用户抬起手指结束手势时发生的巨大差异。
偷看
当用户按下滑块时会发生偷看。在我的 主视图控制器 中,当我创建三个滑块中的每一个时,我将它们注册为使用该主视图控制器进行预览:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
我的视图控制器需要实现
UIViewControllerPreviewingDelegate
以便告诉 iOS 在用户希望预览时弹出什么。在 ChromaTouch 的情况下,它是一个
PeekViewController
,它在 previewingContext(viewControllerForLocation) 中定义:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
PeekViewController
非常基础,包含一个
UILabel
,其文本集基于我编写的用于从
UIColor
中提取 RGB 组件的扩展
。
预览动作
通过向上滑动预览,用户可以将他们的颜色设置为预设。这实现起来超级简单:我的
PeekViewController
需要做的就是返回一个
UIPreviewActionItem
数组,它是我根据预定义颜色枚举数组创建的:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
因为我在
PeekViewController
的构造函数中将主视图控制器作为委托传递,
PeekViewController
中的
updateColor()
方法可以根据作为预览操作选择的颜色将新构造的 HSL 元组传递给它:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
弹出
最后一步是弹出:当用户在预览上深按时,预览将消失(这是由框架管理的)并且主视图控制器再次显示。但是,在这里我想隐藏滑块并使样本全屏显示。一旦用户点击,我希望屏幕恢复到默认状态。
弹出需要
UIViewControllerPreviewingDelegate
中的第二种方法,即
previewingContext(commitViewController)
。在这里,我只是关闭样本上的用户交互并隐藏包含三个滑块的堆栈视图:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
为了响应点击以将用户界面返回到默认值,
ChromaTouchViewController
重新启用样本上的用户交互并取消隐藏进度条:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else
{
return
}
let touchLocation = touch.locationInView(self)
let force = touch.force
let maximumPossibleForce = touch.maximumPossibleForce
let normalisedXPosition = touchLocation.x / frame.width
let normalisedYPosition = touchLocation.y / frame.height
let normalisedZPosition = force / maximumPossibleForce
hsl = HSL(hue: normalisedXPosition,
saturation: normalisedYPosition,
lightness: normalisedZPosition)
}
结论
我开始真正喜欢在日常 iOS 应用程序中查看和弹出。正如我在 上一篇文章 中提到的以及此处所展示的那样,它非常容易实现。
这个项目的所有代码都可以在 我的 GitHub 存储库 中找到。享受!
只是为了好玩...
最后,我无法抗拒我的旧 Core Image 过滤实时视频项目,并拥有一个由 3D Touch 控制的
CIBumpFilter
。此处,力控制凹凸滤镜的比例。
这种愚蠢的分支就在这里
。