如何正确实现可拖动的“抽屉”自定义 UIView

Posted

技术标签:

【中文标题】如何正确实现可拖动的“抽屉”自定义 UIView【英文标题】:How to properly implement a draggable "drawer" custom UIView 【发布时间】:2014-01-10 20:25:56 【问题描述】:

我的视图控制器底部有一个自定义UIView,它充当“抽屉”控件。大约 90 像素的视图是可见的。当用户点击视图时,它会向上动画显示其他选项、​​控件等。再次点击后,视图会动画回到其原始位置。有效地“关闭抽屉”。

我还实现了代码,以便视图通过拖动手势进行跟踪:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

    if(self.frame.origin.y >= 80) 
        UITouch *theTouch = [touches anyObject];
        CGPoint location = [theTouch locationInView:self];
        CGPoint previousLocation = [theTouch previousLocationInView:self];
        CGFloat target = location.y - previousLocation.y;
        self.frame = CGRectOffset(self.frame, 0, target);
    

所有这些都按预期工作。但是,视图无法正确响应快速“轻弹”手势。在touchesEnded:withEvent: 方法中,我正在为用户将手指举到目的地的视图设置动画。如果用户缓慢拖动然后释放,这很有效。但是如果他们快速轻弹,视图仍然需要处理 0.5 秒的持续时间,这对眼睛来说并不平滑。这是所有自定义视图的代码:

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

    if(self.frame.origin.y >= 80) 
        UITouch *theTouch = [touches anyObject];
        CGPoint location = [theTouch locationInView:self];
        CGPoint previousLocation = [theTouch previousLocationInView:self];
        CGFloat target = location.y - previousLocation.y;
        self.frame = CGRectOffset(self.frame, 0, target);
    


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    if(self.frame.origin.y == kDefaultUpY) 
        _comingFromUp = YES;
        _comingFromDown = NO;
    
    else if(self.frame.origin.y == kDefaultDownY) 
        _comingFromUp = NO;
        _comingFromDown = YES;
    


-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 

    //responding to tap
    if(self.frame.origin.y == kDefaultUpY) 
        [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
            self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
         completion:nil];

        return;
    
    else if(self.frame.origin.y == kDefaultDownY) 
        [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
            self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
         completion:nil];

        return;
    

    //responding to drag
    if(_comingFromDown) 
        if(self.frame.origin.y < kDefaultDownY) 
            //dragging up -- go up
            [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
                self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
             completion:nil];
        
        else if(self.frame.origin.y > kDefaultDownY) 
            //dragging down -- stay down
            [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
                self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
             completion:nil];
        
    
    else if(_comingFromUp) 
        if(self.frame.origin.y < kDefaultUpY) 
            //dragging up -- stay up
            [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
                self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
             completion:nil];
        
        else if(self.frame.origin.y > kDefaultUpY) 
            //dragging down -- go down
            [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^
                self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
             completion:nil];
        
    

如何正确响应与“抽屉”视图相关的手势?有没有实现这种功能的标准方法?

【问题讨论】:

【参考方案1】:

使用手势识别器而不是自己实现触摸处理代码。您可以同时附加滑动手势识别器和平移手势识别,并设置平移手势,以便在触发平移之前滑动必须失败。使滑动手势的动画更快。像 0.1 或 0.2 秒这样的时间可能适合滑动手势。

不过需要注意的是:在 ios 7 中,从屏幕底部滑动手势会触发系统设置抽屉从屏幕底部弹出。我们的应用程序 Face Dancer 有一个控制抽屉,我们最近发现在 iOS 7 下很难在不触发系统设置抽屉的情况下向上滑动我们的控制抽屉。我们有一个用于拖动的“拇指”区域,并且我们正在添加一个点击手势,它将在一个步骤中完全显示/隐藏它。

【讨论】:

是什么导致滑动手势失败?我有一个上下滑动手势识别器,还有一个平移手势识别器设置为让滑动失败。但是 pan 选择器永远不会被调用。 如果手势太慢或倾斜,滑动应该会失败。我自己没有这样做,所以我在做一些有根据的猜测。

以上是关于如何正确实现可拖动的“抽屉”自定义 UIView的主要内容,如果未能解决你的问题,请参考以下文章

拖动带有阴影的自定义 UIView - 大小重置和阴影消失

如何将自定义视图拖动为 Xcode 6 中自定义视图的私有属性

Android上的自定义导航抽屉

如何使自定义 UIView 可访问?

如何在 UIView 上绘制点?

如何在android中创建自定义导航抽屉