使用UIScrollView用两根手指滚动

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用UIScrollView用两根手指滚动相关的知识,希望对你有一定的参考价值。

我有一个应用程序,我的主视图接受touchesBegantouchesMoved,因此接受单指触摸和拖动。我想实现一个UIScrollView,我有它的工作,但它覆盖了drags,因此我的contentView永远不会收到它们。我想实现一个UIScrollview,其中双指拖动表示滚动,单指拖动事件传递到我的内容视图,因此它正常执行。我是否需要创建自己的UIScrollView子类?

这是我在appDelegate的代码,我实现了UIScrollView

@implementation MusicGridAppDelegate

@synthesize window;
@synthesize viewController;
@synthesize scrollView;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // Override point for customization after app launch    
    //[application setStatusBarHidden:YES animated:NO];
    //[window addSubview:viewController.view];

    scrollView.contentSize = CGSizeMake(720, 480);
    scrollView.showsHorizontalScrollIndicator = YES;
    scrollView.showsVerticalScrollIndicator = YES;
    scrollView.delegate = self;
    [scrollView addSubview:viewController.view];
    [window makeKeyAndVisible];
}


- (void)dealloc {
    [viewController release];
    [scrollView release];
    [window release];
    [super dealloc];
}
答案

你需要子类UIScrollView(当然!)。然后你需要:

  • 使单指事件转到您的内容视图(简单),和
  • 使双指事件滚动滚动视图(可能很容易,可能很难,可能是不可能的)。

Patrick的建议通常很好:让你的UIScrollView子类知道你的内容视图,然后在触摸事件处理程序中检查手指的数量并相应地转发事件。只需确保(1)您发送到内容视图的事件不会通过响应者链回送到UIScrollView(即确保全部处理它们),(2)尊重触摸事件的常规流程(即touchesBegan,而不是一些{touchesBegan,touchesMoved,touchesEnded},用touchesEnded或touchesCancelled完成,特别是在处理UIScrollView时。 #2可能很棘手。

如果您确定该事件是针对UIScrollView,另一个技巧是让UIScrollView相信您的双指手势实际上是单指手势(因为UIScrollView无法用两根手指滚动)。尝试只将一个手指的数据传递给super(通过过滤(NSSet *)touches参数 - 请注意它只包含已更改的触摸 - 并完全忽略错误手指的事件)。

如果这不起作用,你就麻烦了。从理论上讲,您可以尝试通过创建类似于UITouch的类来创建人工触摸以提供给UIScrollView。底层的C代码不会检查类型,因此可能会将(YourTouch *)转换为(UITouch *),并且您将能够欺骗UIScrollView来处理实际没有发生的触摸。

您可能想要阅读my article on advanced UIScrollView tricks(并在那里看到一些完全不相关的UIScrollView示例代码)。

当然,如果你不能让它工作,总是可以选择手动控制UIScrollView的移动,或使用完全自定义编写的滚动视图。在Three20 library中有TTScrollView类;它对用户来说并不好,但对程序员来说确实感觉很好。

另一答案

这似乎是互联网上这个问题的最佳资源。另一个接近解决方案can be found here

我以一种非常令人满意的方式以不同的方式解决了这个问题,主要是通过将我自己的手势识别器取代到等式中。我强烈建议任何试图达到原始海报要求的效果的人都会考虑使用UIScrollView的侵略性子类化。

以下过程将提供:

  • 包含自定义视图的UIScrollView
  • 用两根手指放大和平移(通过UIPinchGestureRecognizer
  • 所有其他触摸的视图事件处理

首先,假设您有一个视图控制器及其视图。在IB中,使视图成为scrollView的子视图,并调整视图的调整大小规则,以便它不会调整大小。在scrollview的属性中,打开任何显示“bounce”并关闭“delaysContentTouches”的内容。此外,您必须将缩放最小值和最大值设置为默认值1.0以外的值,正如Apple的文档所说,这是缩放工作所必需的。

创建UIScrollView的自定义子类,并使此scrollview成为自定义子类。在视图控制器中为插件添加插座并连接它们。你现在完全配置好了。

您需要将以下代码添加到UIScrollView子类中,以便透明地传递触摸事件(我怀疑这可以更优雅地完成,甚至可能完全绕过子类):

#pragma mark -
#pragma mark Event Passing

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.nextResponder touchesEnded:touches withEvent:event];
}
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
    return NO;
}

将此代码添加到视图控制器:

- (void)setupGestures {
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    [self.view addGestureRecognizer:pinchGesture];
    [pinchGesture release];
}

- (IBAction)handlePinchGesture:(UIPinchGestureRecognizer *)sender {
if ( sender.state == UIGestureRecognizerStateBegan ) {
    //Hold values
    previousLocation = [sender locationInView:self.view];
    previousOffset = self.scrollView.contentOffset;
    previousScale = self.scrollView.zoomScale;
} else if ( sender.state == UIGestureRecognizerStateChanged ) {
    //Zoom
    [self.scrollView setZoomScale:previousScale*sender.scale animated:NO];

    //Move
    location = [sender locationInView:self.view];
    CGPoint offset = CGPointMake(previousOffset.x+(previousLocation.x-location.x), previousOffset.y+(previousLocation.y-location.y));
    [self.scrollView setContentOffset:offset animated:NO];  
} else {
    if ( previousScale*sender.scale < 1.15 && previousScale*sender.scale > .85 )
        [self.scrollView setZoomScale:1.0 animated:YES];
}

}

请注意,在此方法中,您必须在视图控制器的类文件中定义许多属性:

  • CGFloat previousScale;
  • CGPoint previousOffset;
  • CGPoint previousLocation;
  • CGPoint location;

好的就是它!

不幸的是,我无法让scrollView在手势中显示其滚动条。我尝试了所有这些策略:

//Scroll indicators
self.scrollView.showsVerticalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
[self.scrollView flashScrollIndicators];
[self.scrollView setNeedsDisplay];

我真正喜欢的一件事是,如果你看看最后一行,你会注意到它抓住了大约100%的最终变焦,并将其四舍五入。您可以调整容差水平;我在Pages的缩放行为中看到了这一点,并认为这将是一个很好的接触。

另一答案

看看我的solution

#import “JWTwoFingerScrollView.h”

@implementation JWTwoFingerScrollView

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        for (UIGestureRecognizer* r in self.gestureRecognizers) {
            NSLog(@“%@”,[r class]);
            if ([r isKindOfClass:[UIPanGestureRecognizer class]]) {
                [((UIPanGestureRecognizer*)r) setMaximumNumberOfTouches:2];
                [((UIPanGestureRecognizer*)r) setMinimumNumberOfTouches:2];
            }
        }
    }
    return self;
}

-(void)firstTouchTimerFired:(NSTimer*)timer {
    [self setCanCancelContentTouches:NO];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self setCanCancelContentTouches:YES];
    if ([event allTouches].count == 1){
        touchesBeganTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(firstTouchTimerFired:) userInfo: nil repeats:NO];
        [touchesBeganTimer retain];
        [touchFilter touchesBegan:touches withEvent:event];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [touchFilter touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@“ended %i”,[event allTouches].count);
    [touchFilter touchesEnded:touches withEvent:event];
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@“canceled %i”,[event allTouches].count);
    [touchFilter touchesCancelled:touches withEvent:event];
}

@end

它不会延迟第一次触摸,并且在用户使用后用两个手指触摸时不会停止。它仍允许使用计时器取消刚开始的一触式事件。

另一答案

是的,您需要继承UIScrollView并覆盖其-touchesBegan:-touchesEnded:方法以传递“up”。这可能还涉及具有UIView成员变量的子类,以便它知道将触摸传递给它的意义。

另一答案

我把它放在viewDidLoad方法中,这实现了处理两个触摸平移行为的滚动视图和另一个处理一触摸平移行为的平移手势处理程序 - >

scrollView.panGestureRecognizer.minimumNumberOfTouches = 2

let panGR = UIPanGestureRecognizer(target: self, action: #selector(ViewController.handlePan(_:)))
panGR.minimumNumberOfTouches = 1
panGR.maximumNumberOfTouches = 1

scrollView.gestureRecognizers?.append(panGR)

并且在handlePan方法中,这是一个附加到ViewController的函数,只有一个print语句来验证是否正在输入该方法 - >

@IBAction func handlePan(_ sender: UIPanGestureRecognizer) {
    print("Entered handlePan numberOfTuoches: (sender.numberOfTouches)")
}

HTH

另一答案

Kenshi在Swift 4中的回答

for gestureRecognizer: UIGestureRecognizer in self.gestureRecognizers! {
    if (gestureRecognizer is UIPanGestureRecognizer) {
        let panGR = gestureRecognizer as? UIPanGestureRecognizer
        panGR?.minimumNumberOfTouches = 2
    }
}
另一答案

在SDK 3.2中,UIScrollView的触摸处理使用Gesture Recognizers处理。

如果要进行双指平移而不是默认的单指平移,可以使用以下代码:

for (UIGestureRecognizer *gestureRecognizer in scrollView.gestureRecognizers) {     
    if ([gestureRecognizer  isKindOfClass:[UIPanGestureRecognizer class]]) {
        UIPanGestureRecognizer *panGR = (UIPanGestureRecognizer *) gestureRecognizer

以上是关于使用UIScrollView用两根手指滚动的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView 放大时用 1 根手指平移

如何区分 UIScrollView 中的平移和滚动

让 UIScrollView 放弃 UITouch

试图在 uiscrollview 内部绘制

从 UIScrollview 向前触摸和手势到视图

菜单抽屉仅用两根手指滑动即可打开 DrawerLayout