簡體   English   中英

轉發到UIScrollView

[英]Forward touches to a UIScrollView

我有兩個視圖控制器。 視圖控制器A具有UIScrollView並呈現視圖控制器B.該演示是交互式的,並由scrollView.contentOffset控制。

我想整合一個交互式消除過渡:當平移時,ViewController B應該以交互方式被解雇。 交互式dismiss轉換還應控制ViewController的scrollView。

我的第一次嘗試是使用UIPanGestureRecognizer並根據平移距離設置scrollView.contentOffset 這是有效的,但是當平移手勢結束時,必須將scrollView偏移設置為最終位置的動畫。 使用-[UIScrollView setContentOffset:animated:不是一個很好的解決方案,因為它使用線性動畫,不會考慮當前的平移速度,也不會很好地減速。

所以我認為應該可以將我的平移手勢識別器中的觸摸事件提供到滾動視圖中。 這應該給我們所有漂亮的滾動視圖動畫行為。

我嘗試覆蓋我的UIPanGestureRecognizer子類中的-touchesBegan/Moved/Ended/Cancelled withEvent:方法,如下所示:

- (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

在閱讀了描述UIScrollView事件流程的有趣答案后,我得出的結論是,嘗試從手勢識別器“遠程控制”滾動視圖可能很難實現,因為觸摸在被路由到視圖和手勢識別器時會發生變化。 由於UITouch不符合NSCopying我們也無法克隆觸摸事件,以便稍后以未修改狀態發送它們。

雖然沒有真正解決我要求的問題,但我找到了解決方法來完成我需要的工作。 我剛剛添加了一個滾動視圖來查看控制器B並將其與VC A的滾動視圖同步(在垂直滾動時添加到視圖層次結構中):

// delegate of VC B's scrollView
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
    scrollViewA.contentOffset = scrollView.contentOffset;
}

感謝Friedrich Markgraf 提出這個想法

嘗試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
    } 
}

您可能還需要在UIScrollView的超級視圖上覆蓋pointInside:withEvent: ,具體取決於您的布局。

有關更多信息,請參閱以下問題: 超出uiview界限的交互

也許您可以嘗試在UIPanGesture委托中使用該方法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

我不知道它會如何反應,因為你的觀點是模態的。 但也許它可以成功,如果沒有直接使用beginTouch等觸摸,這是一個痛苦的屁股。

告訴我它是否有幫助?

作為@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)
    }
}

我在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)
  }
}

完美的工作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM