我有两个视图控制器。视图控制器A具有UIScrollView
并呈现视图控制器B.演示文稿是交互式的,由scrollView.contentOffset
控制。
我想整合一个交互式消除过渡:当平移时,ViewController B应该以交互方式被解雇。交互式关闭转换还应控制ViewController A scrollView。
我的第一次尝试是使用UIPanGestureRecognizer
并根据平移距离设置scrollView.contentOffset
。这是有效的,但是当平移手势结束时,必须将scrollView偏移设置为最终位置的动画。使用-[UIScrollView setContentOffset:animated:
不是一个好的解决方案,因为它使用线性动画,不考虑当前的平移速度,并且不会很好地减速。
所以我认为应该可以将我的平移手势识别器中的触摸事件提供给滚动视图。这应该给我们所有漂亮的滚动视图动画行为。
我尝试覆盖-touchesBegan/Moved/Ended/Cancelled withEvent:
子类中的UIPanGestureRecognizer
方法,如下所示:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[scrollView touchesBegan:touches withEvent:event];
[scrollView.panGestureRecognizer touchesBegan:touches withEvent:event];
[super touchesBegan:touches withEvent:event];
}
但显然阻止滚动视图进入tracking
模式。 (它确实转到了dragging = YES
但是关于它。)我验证了scrollView是userInteractionEnabled
,未隐藏并添加到视图层次结构中。
那么如何将触摸事件转发到UIScrollView
?
答案 0 :(得分:12)
在阅读了描述UIScrollView
事件流的interesting answer之后,我得出的结论是,尝试从手势识别器“远程控制”滚动视图可能很难实现,因为触摸是在被路由到视图和手势识别器时发生变异。由于UITouch
不符合NSCopying
,因此我们也无法克隆触摸事件,以便稍后以未修改状态发送它们。
虽然没有真正解决我要求的问题,但我找到了解决方法来完成我需要的工作。我刚刚添加了一个滚动视图来查看控制器B,并将其与VC A的滚动视图同步(在垂直滚动时添加到视图层次结构中):
// delegate of VC B's scrollView
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
scrollViewA.contentOffset = scrollView.contentOffset;
}
感谢Friedrich Markgraf came up with the idea。
答案 1 :(得分:3)
尝试对UIScrollView
进行子类化并覆盖hitTest:withEvent:
,以便UIScrollView
选择超出其范围的触摸。然后你可以免费获得所有漂亮的UIScrollView
动画。像这样:
@interface MagicScrollView : UIScrollView
@end
@implementation MagicScrollView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Intercept touches 100pt outside this view's bounds on all sides. Customize this for the touch area you want to intercept
if (CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point)) {
return self;
}
return nil;
}
@end
或者在Swift(感谢Edwin Vermeer!)
class MagicScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if CGRectContainsPoint(CGRectInset(self.bounds, -100, -100), point) {
return self
}
return nil
}
}
您可能还需要覆盖pointInside:withEvent:
超级视图上的UIScrollView
,具体取决于您的布局。
有关详细信息,请参阅以下问题:interaction beyond bounds of uiview
答案 2 :(得分:0)
也许您可以尝试在UIPanGesture委托中使用该方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
我不知道它会如何反应,因为你的观点是模态的。但也许它可以成功,如果没有直接使用beginTouch等触摸,这是一个痛苦的屁股。
告诉我它是否有帮助?
答案 3 :(得分:0)
作为@johnboiles解决方案的补充。当您捕获整个rect时,将跳过对scrollview内部元素的触摸。您可以添加aditional触摸区域,对于普通的scrollview,只需将其传递给super.hitTest,如下所示:
class MagicScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if CGRectContainsPoint(CGRect(x: self.bounds.origin.x - 30, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) {
return self
}
if CGRectContainsPoint(CGRect(x: self.bounds.origin.x + self.bounds.size.width, y: self.bounds.origin.y, width: 30, height: self.bounds.size.height), point) {
return self
}
return super.hitTest(point, withEvent: event)
}
}
答案 4 :(得分:0)
我在superview里面有一个滚动视图,有左右边框。为了使这些边框可滚动,这是我的方法:
class CustomScrollView: UIScrollView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let view = super.hitTest(point, withEvent: event) {
return view
}
guard let superview = superview else {
return nil
}
return superview.hitTest(superview.convertPoint(point, fromView: self), withEvent: event)
}
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
if super.pointInside(point, withEvent: event) {
return true
}
guard let superview = superview else {
return false
}
return superview.pointInside(superview.convertPoint(point, fromView: self), withEvent: event)
}
}
完美运作