我有一个带有两个UIPickerView子视图的UIView,每个子视图旋转60度,一个顺时针旋转,一个逆时针旋转。我想根据用户滑动的方向分别滚动每个pickerview。由于一个位于另一个顶部,因此只能滚动顶部的pickerview。因此,我希望能够在用户沿其方向滑动时滚动底部的pickerview。
我找到的最接近的答案是覆盖hitTest,但是后来我无法确定滑动方向。我想我必须以某种方式使用touchesBegan,touchesMoved和touchesEnded来确定滑动方向。
我的第一个想法是这样的,似乎没有用
var startPoint: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = touches.first!.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint else { return }
let endPoint = touches.first!.location(in: self)
//This condition is arbitrary
//The actual condition will be more complex
if startPoint.y > endPoint.y {
pickerViewA.isUserInteractionEnabled = true
} else {
pickerViewB.isUserInteractionEnabled = true
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
pickerViewA.isUserInteractionEnabled = false
pickerViewB.isUserInteractionEnabled = false
startPoint = nil
}
答案 0 :(得分:0)
此问题的可能解决方案之一是使用屏幕大小的透明视图(您的根ViewController):
transparentView3 = MyTransparentView(frame: view.bounds)
transparentView3?.backgroundColor = UIColor.clear
view.addSubview(transparentView3!)
您可以为此透明视图创建一个类,并在那里覆盖触摸事件:
class MyTransparentView: UIView {
weak var delegate: MyTransparentViewDelegate?
var swipePoint1: CGPoint?
var swipeTime1: Double?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
swipePoint1 = touches.first?.location(in: self)
swipeTime1 = event?.timestamp
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if let swipePoint2 = touches.first?.location(in: self),
let swipeTime2 = event?.timestamp,
let swipePoint1 = swipePoint1,
let swipeTime1 = swipeTime1 {
if swipePoint1.x > swipePoint2.x && swipeTime2-swipeTime1 < 0.5 {
delegate?.importantSwipeOccured()
}
}
}
}
与使用UIGestureRecognizers相比,这将提供更多的灵活性。
现在,包含两个选择器和一个透明视图的ViewController可以成为透明视图的委托,并将消息重定向到您的选择器:
protocol MyTransparentViewDelegate: AnyObject {
func importantSwipeOccured()
}
func importantSwipeOccured() {
let color2 = rotatedView2!.backgroundColor
rotatedView2!.backgroundColor = rotatedView1!.backgroundColor
rotatedView1!.backgroundColor = color2
}
完整的示例可以在这里找到,您可以运行项目或仅使用以下代码:https://github.com/gatamar/so61033169/blob/master/so61033169/ViewController.swift
答案 1 :(得分:0)
在重叠视图的区域上发生触摸序列时,手势之间实际上存在着争夺该触摸序列的竞争。
也许尝试一下?
我们在顶部放置了一个透明视图,并为其分配了自定义手势识别器。想法是使此手势充当一种中介,该中介确定哪个选择器视图获取触摸顺序;而不是他们彼此争夺,而头号总是赢。
每个选择器视图都有其自己的手势,我们将告诉两者都要求自定义手势失败。这意味着我们的自定义手势必须首先失败,然后这些手势才有机会获取触摸顺序。
在自定义手势的代码中,我们将始终将其状态设置为“失败”,从而允许将触摸序列传递给其他手势。但是在失败之前,我们将通过在两个选择器视图上相应地设置userInteractionEnabled来确定哪个选择器视图应获取此触摸顺序。当然,这是希望如果我们在视图上禁用userInteraction,则该视图将不会获得向下传递的触摸顺序。大声笑,在这种情况下,我们将提出其他建议。
以下是我们自定义手势的代码,可帮助您入门:
import UIKit.UIGestureRecognizerSubclass
class BrokerGesture: UIPanGestureRecognizer {
private var startPoint: CGPoint!
private var views: [UIView]
override init(target: Any?, action: Selector?, views: [UIView]) {
self.views = views
super.init(target: target, action: action)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
self.startPoint = touches.first!.location(in: self.view!.superview)
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if self.state == .possible {
let loc = touches.first!.location(in: self.view!.superview)
let deltaX = abs(loc.x - self.startPoint.x)
let deltaY = abs(loc.y - self.startPoint.y)
// Your code here:
// - Use deltaX and deltaY
// - Set userInteraction on both views accordingly
self.state = .failed
} else {
super.touchesMoved(touches, with: event)
}
// Don't call super here! It will just set the state to .began! and we don't want to do that.
}
}
答案 2 :(得分:0)
尝试了许多不同的尝试后,我回过头来重新阅读了Matt Neuburg在iOS上的书中的“ Touches”一章,解决方案变得清晰起来。
基本上,屏幕上的手指实例由UITouch表示。 多点触摸序列从第一个手指放到屏幕上时开始,到屏幕上没有手指时结束。在整个多点触摸过程中,一个UITouch对象本质上表示同一根手指。 系统将所有这些UITouch对象打包在一个称为UIEvent的信封中。这被发送到我们的UIWindow,UIWindow使用命中测试和填充将其发送到正确的视图。这是通过其sendEvent(:)方法完成的。
如果我们子类化UIWindow,则可以重写sendEvent(:)来拦截事件。我们要做的就是查看事件中的触摸,并确定应该滚动哪个选择器视图。我们将选择器视图置于最前面,然后调用super,它将正常发送事件。
IterativeImputer().transform(X)
哦,确保您的AppDelegate创建了UIWindow子类的实例,并为其分配了根视图控制器。
X
我对其进行了测试,并且可以正常工作。无论如何,希望能有所帮助:)