- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我需要在我的 UI 的一个非常特定的区域内检测敲击 - 一个倾斜的钢琴键,这里以蓝色突出显示,但我目前的设置方式,检测不准确:
因为这不是一个简单的矩形,所以我不能只使用常规按钮。相反,我决定使用 PaintCode
plugin
直接从 Sketch
创建贝塞尔曲线路径。
在你说这个问题重复之前:是的,我知道 this question that was asked previously ,事实上,我非常依赖 Abhimanyu Rathore 和 Diogo Souza 的回答来获得基本功能。
Sketch 图像由 3 个部分组成,blackKeyTop
、blackKeySide
和 blackKeyBottom
,这就是为什么 PaintCode 看起来创建了 3 个单独的贝塞尔曲线路径:
//
// StyleKit.swift
//
// Created on May 2, 2019.
//
// Generated by PaintCode Plugin for Sketch
// http://www.paintcodeapp.com/sketch
//
import UIKit
var currentBezierPath = UIBezierPath()
class StyleKit: NSObject {
//MARK: - Canvas Drawings
/// Page 1
class func drawA5(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 516), resizing: ResizingBehavior = .aspectFit) {
/// General Declarations
let context = UIGraphicsGetCurrentContext()!
let baseTransform = context.userSpaceToDeviceSpaceTransform.inverted()
/// Resize to Target Frame
context.saveGState()
let resizedFrame = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 516), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 516)
/// A#5 highlight
do {
context.saveGState()
context.translateBy(x: 96.5, y: 260)
context.scaleBy(x: -1, y: 1)
context.translateBy(x: -87.5, y: -250)
/// black key bottom
let blackKeyBottom = UIBezierPath()
blackKeyBottom.move(to: CGPoint(x: 66.43, y: 85.19))
blackKeyBottom.addCurve(to: CGPoint(x: 72.75, y: 85.19), controlPoint1: CGPoint(x: 70.35, y: 85.49), controlPoint2: CGPoint(x: 72.46, y: 85.49))
blackKeyBottom.addCurve(to: CGPoint(x: 72.04, y: 81.34), controlPoint1: CGPoint(x: 73.04, y: 84.89), controlPoint2: CGPoint(x: 72.8, y: 83.61))
blackKeyBottom.addLine(to: CGPoint(x: 58.18, y: 21.72))
blackKeyBottom.addCurve(to: CGPoint(x: 49.42, y: 7.99), controlPoint1: CGPoint(x: 54.67, y: 14.84), controlPoint2: CGPoint(x: 51.75, y: 10.26))
blackKeyBottom.addCurve(to: CGPoint(x: 28.57, y: 0), controlPoint1: CGPoint(x: 43.73, y: 2.43), controlPoint2: CGPoint(x: 36.47, y: 0))
blackKeyBottom.addCurve(to: CGPoint(x: 7.87, y: 8.55), controlPoint1: CGPoint(x: 20.59, y: 0), controlPoint2: CGPoint(x: 13.33, y: 3))
blackKeyBottom.addCurve(to: CGPoint(x: 0, y: 21.2), controlPoint1: CGPoint(x: 5.71, y: 10.76), controlPoint2: CGPoint(x: 3.08, y: 14.97))
blackKeyBottom.addLine(to: CGPoint(x: 1.33, y: 81.34))
blackKeyBottom.addLine(to: CGPoint(x: 1.33, y: 85.66))
blackKeyBottom.addLine(to: CGPoint(x: 4.33, y: 85.66))
blackKeyBottom.addLine(to: CGPoint(x: 66.43, y: 85.19))
blackKeyBottom.close()
context.saveGState()
context.translateBy(x: 2.09, y: 411.53)
// Warning: Blur effects are not supported.
blackKeyBottom.usesEvenOddFillRule = true
context.saveGState()
context.beginPath()
context.addPath(blackKeyBottom.cgPath)
context.addRect(blackKeyBottom.bounds.insetBy(dx: -74, dy: -74))
context.clip(using: .evenOdd)
context.translateBy(x: -147.87, y: 0)
do {
let baseZero = context.convertToDeviceSpace(CGPoint.zero).applying(baseTransform)
let baseOne = context.convertToDeviceSpace(CGPoint(x: 1, y: 1)).applying(baseTransform)
let baseOffset = context.convertToDeviceSpace(CGPoint(x: 147.87, y: 0)).applying(baseTransform)
let shadowOffset = CGSize(width: baseOffset.x - baseZero.x, height: baseOffset.y - baseZero.y)
let shadowBlur: CGFloat = 74 * min(baseOne.x - baseZero.x, baseOne.y - baseZero.y)
context.setShadow(offset: shadowOffset, blur: shadowBlur, color: UIColor(hue: 0.616, saturation: 1, brightness: 1, alpha: 0.5).cgColor)
}
UIColor.black.setFill()
blackKeyBottom.fill()
context.restoreGState()
blackKeyBottom.usesEvenOddFillRule = true
UIColor(white: 0.847, alpha: 1).setFill()
blackKeyBottom.fill()
UIColor(hue: 0.622, saturation: 0.975, brightness: 0.831, alpha: 1).setFill()
blackKeyBottom.fill()
context.restoreGState()
currentBezierPath.append(blackKeyBottom)
/// black key side
let blackKeySide = UIBezierPath()
blackKeySide.move(to: CGPoint(x: 14.38, y: 498.36))
blackKeySide.addLine(to: CGPoint(x: 15.36, y: 495.31))
blackKeySide.addLine(to: CGPoint(x: 28.85, y: 422.78))
blackKeySide.addLine(to: CGPoint(x: 111.37, y: 33.73))
blackKeySide.addLine(to: CGPoint(x: 89.45, y: 0))
blackKeySide.addLine(to: CGPoint(x: 11.65, y: 358.27))
blackKeySide.addLine(to: CGPoint(x: 0.91, y: 408.5))
blackKeySide.addLine(to: CGPoint(x: 0, y: 415.52))
blackKeySide.addLine(to: CGPoint(x: 0.54, y: 422.78))
blackKeySide.addLine(to: CGPoint(x: 5.44, y: 457.06))
blackKeySide.addLine(to: CGPoint(x: 11.65, y: 492.02))
blackKeySide.addLine(to: CGPoint(x: 14.38, y: 498.36))
blackKeySide.close()
context.saveGState()
context.translateBy(x: 63.02, y: 1.09)
// Warning: Blur effects are not supported.
blackKeySide.usesEvenOddFillRule = true
context.saveGState()
context.beginPath()
context.addPath(blackKeySide.cgPath)
context.addRect(blackKeySide.bounds.insetBy(dx: -74, dy: -74))
context.clip(using: .evenOdd)
context.translateBy(x: -186.37, y: 0)
do {
let baseZero = context.convertToDeviceSpace(CGPoint.zero).applying(baseTransform)
let baseOne = context.convertToDeviceSpace(CGPoint(x: 1, y: 1)).applying(baseTransform)
let baseOffset = context.convertToDeviceSpace(CGPoint(x: 186.37, y: 0)).applying(baseTransform)
let shadowOffset = CGSize(width: baseOffset.x - baseZero.x, height: baseOffset.y - baseZero.y)
let shadowBlur: CGFloat = 74 * min(baseOne.x - baseZero.x, baseOne.y - baseZero.y)
context.setShadow(offset: shadowOffset, blur: shadowBlur, color: UIColor(hue: 0.616, saturation: 1, brightness: 1, alpha: 0.5).cgColor)
}
UIColor.black.setFill()
blackKeySide.fill()
context.restoreGState()
blackKeySide.usesEvenOddFillRule = true
UIColor(hue: 0.622, saturation: 1, brightness: 0.745, alpha: 0.77).setFill()
blackKeySide.fill()
context.restoreGState()
currentBezierPath.append(blackKeySide)
/// black key top
let blackKeyTop = UIBezierPath()
blackKeyTop.move(to: CGPoint(x: 8.55, y: 376.64))
blackKeyTop.addLine(to: CGPoint(x: 1.08, y: 409.47))
blackKeyTop.addLine(to: CGPoint(x: 0, y: 425.43))
blackKeyTop.addLine(to: CGPoint(x: 0.6, y: 437.48))
blackKeyTop.addCurve(to: CGPoint(x: 6.37, y: 421.98), controlPoint1: CGPoint(x: 1.3, y: 431.07), controlPoint2: CGPoint(x: 3.22, y: 425.91))
blackKeyTop.addCurve(to: CGPoint(x: 13.55, y: 415.73), controlPoint1: CGPoint(x: 7.95, y: 420.01), controlPoint2: CGPoint(x: 10.34, y: 417.93))
blackKeyTop.addCurve(to: CGPoint(x: 18.28, y: 412.98), controlPoint1: CGPoint(x: 15.71, y: 414.18), controlPoint2: CGPoint(x: 17.29, y: 413.26))
blackKeyTop.addCurve(to: CGPoint(x: 23.32, y: 411.58), controlPoint1: CGPoint(x: 19.27, y: 412.7), controlPoint2: CGPoint(x: 20.96, y: 412.23))
blackKeyTop.addLine(to: CGPoint(x: 30.23, y: 410.75))
blackKeyTop.addLine(to: CGPoint(x: 37.04, y: 411.58))
blackKeyTop.addLine(to: CGPoint(x: 44.39, y: 414.18))
blackKeyTop.addLine(to: CGPoint(x: 52.05, y: 419.89))
blackKeyTop.addLine(to: CGPoint(x: 57.52, y: 428.52))
blackKeyTop.addLine(to: CGPoint(x: 65.5, y: 451.2))
blackKeyTop.addLine(to: CGPoint(x: 62.73, y: 420.81))
blackKeyTop.addLine(to: CGPoint(x: 63.2, y: 408.02))
blackKeyTop.addLine(to: CGPoint(x: 65.7, y: 396.36))
blackKeyTop.addLine(to: CGPoint(x: 151.29, y: 3.88))
blackKeyTop.addCurve(to: CGPoint(x: 151.73, y: 0.61), controlPoint1: CGPoint(x: 151.69, y: 1.96), controlPoint2: CGPoint(x: 151.84, y: 0.87))
blackKeyTop.addCurve(to: CGPoint(x: 149.11, y: 0), controlPoint1: CGPoint(x: 151.62, y: 0.35), controlPoint2: CGPoint(x: 150.75, y: 0.14))
blackKeyTop.addLine(to: CGPoint(x: 101.04, y: 0))
blackKeyTop.addCurve(to: CGPoint(x: 98.87, y: 2.39), controlPoint1: CGPoint(x: 100.09, y: 0.38), controlPoint2: CGPoint(x: 99.37, y: 1.18))
blackKeyTop.addCurve(to: CGPoint(x: 96.96, y: 9.3), controlPoint1: CGPoint(x: 98.38, y: 3.6), controlPoint2: CGPoint(x: 97.74, y: 5.9))
blackKeyTop.addLine(to: CGPoint(x: 8.55, y: 376.64))
blackKeyTop.close()
context.saveGState()
context.translateBy(x: 0.96, y: 0.84)
// Warning: Blur effects are not supported.
blackKeyTop.usesEvenOddFillRule = true
context.saveGState()
context.beginPath()
context.addPath(blackKeyTop.cgPath)
context.addRect(blackKeyTop.bounds.insetBy(dx: -55, dy: -55))
context.clip(using: .evenOdd)
context.translateBy(x: -247.76, y: 0)
do {
let baseZero = context.convertToDeviceSpace(CGPoint.zero).applying(baseTransform)
let baseOne = context.convertToDeviceSpace(CGPoint(x: 1, y: 1)).applying(baseTransform)
let baseOffset = context.convertToDeviceSpace(CGPoint(x: 247.76, y: 0)).applying(baseTransform)
let shadowOffset = CGSize(width: baseOffset.x - baseZero.x, height: baseOffset.y - baseZero.y)
let shadowBlur: CGFloat = 15 * min(baseOne.x - baseZero.x, baseOne.y - baseZero.y)
context.setShadow(offset: shadowOffset, blur: shadowBlur, color: UIColor(hue: 0.622, saturation: 1, brightness: 0.714, alpha: 0.5).cgColor)
}
context.beginTransparencyLayer(auxiliaryInfo: nil)
do {
UIColor.black.setFill()
blackKeyTop.fill()
context.saveGState()
blackKeyTop.lineWidth = 8
UIColor.black.setStroke()
blackKeyTop.stroke()
context.restoreGState()
}
context.endTransparencyLayer()
context.restoreGState()
blackKeyTop.usesEvenOddFillRule = true
UIColor(white: 1, alpha: 0.8).setFill()
blackKeyTop.fill()
UIColor(hue: 0.616, saturation: 1, brightness: 1, alpha: 0.72).setFill()
blackKeyTop.fill()
context.restoreGState()
currentBezierPath.append(blackKeyTop)
context.restoreGState()
}
context.restoreGState()
}
//MARK: - Canvas Images
/// Page 1
class func imageOfA5() -> UIImage {
struct LocalCache {
static var image: UIImage!
}
if LocalCache.image != nil {
return LocalCache.image
}
var image: UIImage
UIGraphicsBeginImageContextWithOptions(CGSize(width: 200, height: 516), false, 0)
StyleKit.drawA5()
image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
LocalCache.image = image
return image
}
//MARK: - Resizing Behavior
enum ResizingBehavior {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
var scales = CGSize.zero
scales.width = abs(target.width / rect.width)
scales.height = abs(target.height / rect.height)
switch self {
case .aspectFit:
scales.width = min(scales.width, scales.height)
scales.height = scales.width
case .aspectFill:
scales.width = max(scales.width, scales.height)
scales.height = scales.width
case .stretch:
break
case .center:
scales.width = 1
scales.height = 1
}
var result = rect.standardized
result.size.width *= scales.width
result.size.height *= scales.height
result.origin.x = target.minX + (target.width - result.width) / 2
result.origin.y = target.minY + (target.height - result.height) / 2
return result
}
}
}
如您所见,我添加了一个变量 currentBezierPath,并将黑键绘图的 3 个独立部分附加到它(因为我希望能够检测到黑键上任何位置的点击,而不是只是顶部)。
然后我有一个自定义类,在一个单独的 .swift 文件中有一个占位符名称 FromPaintCode(目前):
import UIKit
@IBDesignable
class FromPaintCode: UIView {
override func draw(_ rect: CGRect) {
StyleKit.drawA5(frame: self.bounds)
}
//MARK:- Hit TAP
@objc public func tapDetected(tapRecognizer: UITapGestureRecognizer) {
let tapLocation: CGPoint = tapRecognizer.location(in: self)
self.hitTest(tapLocation: CGPoint(x: tapLocation.x, y: tapLocation.y))
}
func hitTest(tapLocation: CGPoint) {
let path: UIBezierPath = currentBezierPath
if path.contains(tapLocation) {
print("tap inside bezier path detected!")
} else {
print("tap outside bezier path detected!")
}
}
}
... 然后我有一个简单的 UIView
连接到我的主 ViewController 中名为“aSharp5ViewOutlet
”的 socket ,我正在添加一个
:viewDidLoad()
中的 UITapGestureRecognizer
let tapRecognizer = UITapGestureRecognizer(target: aSharp5ViewOutlet, action: #selector(aSharp5ViewOutlet.tapDetected(tapRecognizer:)))
aSharp5ViewOutlet.addGestureRecognizer(tapRecognizer)
问题是目前敲击识别不准确:黑色(蓝色)键的底部似乎始终返回成功的敲击,但顶部和侧面看起来参差不齐,并且在键的中间附近我得到“敲击检测到外部贝塞尔路径!”信息。此外,我收到错误的“检测到贝塞尔曲线路径内部的点击!”稍微向外,在键的右侧...
我已经在不添加/附加贝塞尔曲线路径的情况下进行了尝试(例如,只关注键的顶部),但遇到了同样的问题。
关于如何解决这个问题有什么建议吗?
编辑:
我决定选择一条更简单的路线,并使用简单的矩形 (UIButtons) 来近似钢琴键,这些矩形旋转一定角度以匹配图像中的角度。有点像这样:
let testTransform = CGAffineTransform(rotationAngle: .pi * 0.93)
testButton.transform = testTransform
最佳答案
亲爱的@Matvey,你在某些方面做得很好:-
FromPaintCode
的 UIView
。UIViewController
上添加 FromPaintCode
。FromPaintCode
上添加 UITapGestureRecognizer
。我在想的问题是
:-
<强>1。 StyleKit 计算函数,我们在 View 中添加所有内容:-
覆盖 func draw(_ rect: CGRect) {
StyleKit.drawA5(框架:self.bounds)
}
<强>2。我们从 Storyboard创建的 UIView。
2 解决方案可以是:-
解决方案 1:-
<强>1。首先,我们将在 UIViewController
中以编程方式制作一个简单的 UIView:-
let testView = UIView()
testView.frame = CGRect.init(x: 0, y: 0, width: 300, height: 300)
testView.backgroundColor = .red
testView.center = self.view.center
self.view.addSubview(testView.center)
<强>2。在您的 UIViewController
中添加全局 currentBezierPath
:- var currentBezierPath = UIBezierPath!
然后我们将在这个**testView**
中使用currentBezierPath绘制一个简单的CAShapeLayer:-
currentBezierPath = UIBezierPath(ovalInRect: testView.bounds)
let shapeLayer = CAShapeLayer()
shapeLayer.path = currentBezierPath.CGPath
shapeLayer.fillColor = UIColor(red: 0.5, green: 1, blue: 0.5, alpha: 1).CGColor
shapeLayer.strokeColor = UIColor.blackColor().CGColor
shapeLayer.lineWidth = 2.0
testView.layer.addSublayer(shapeLayer)
在此测试 View 上添加手势:-
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.tapDetected(tapRecognizer:)))
testView.addGestureRecognizer(tapRecognizer)
4.在UIViewController
中注册Tap函数:-
public func tapDetected(tapRecognizer:UITapGestureRecognizer){
let tapLocation:CGPoint = tapRecognizer.location(in: testView)
self.hitTest(tapLocation: CGPoint(x: tapLocation.x, y: tapLocation.y))}
<强>5。在没有 StyleKit 帮助的情况下尝试使用简单样式的 Hit Test 仅进行一种路径测试:-
private func hitTest(tapLocation:CGPoint){
let path:UIBezierPath = self.currentBezierPath
if path.contains(tapLocation){
//tap detected do what ever you want ..;)
}else{
//ooops you taped on other position in view
}}
重要信息:-
如果上面的练习会给你准确性,那么我们需要查看 StyleKit 并通过将 UIGraphicsGetCurrentContext 替换为 CAShapeLayer 来编辑它,因为图层正在添加到 UIView 上并由 UITapGestureRecognizer 正确检测内部 View 。
解决方案 2:-
在您的 Context 案例中,只需在 UIViewController 中使用简单的触摸事件
与您分享一个问题和解决方案:-
关于ios - 为什么贝塞尔曲线路径中的 hotTest/tap 检测会在以下情况下产生不准确的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55963229/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!