ios - 如何制作像谷歌地图应用程序那样的向上滑动面板?

标签 ios objective-c

我正在寻找类似 AndroidSlidingUpPanel 的内容适用于 iOS。我找到了 MBPullDownController ,但它需要两个 ViewControllers 才能使用,并且需要对我正在努力实现的应用程序的架构进行重大更改。

我只想要在现有 View Controller 中添加 subview 的东西。我该怎么做?

最佳答案

我在我的 iOS 应用程序中经常使用向上滑动面板,我发现诀窍是将自定义 View 添加到 Storyboard(或 xib 文件)中的 View Controller ,但要设置它的框架,以便它是离开屏幕。您可以使用 layout constraints 确保 View 在任何设备上都处于屏幕外。 .

那么这只是一个在适当的时候在屏幕上动画 View 的情况。例如:

- (IBAction)showPanel:(id)sender
{
    // panelShown is an iVar to track the panel state...
    if (!panelShown) {
        // myConstraint is an IBOutlet to the appropriate constraint...
        // Use this method for iOS 8+ otherwise use a frame based animation...
        myConstraint.constant -= customView.frame.size.height;
        [UIView animateWithDuration:0.5 animations:^{
            [self.view setNeedsLayout];
        }];
    }  
    else { 
        myConstraint.constant += customView.frame.size.height;
        [UIView animateWithDuration:0.5 animations:^{
            [self.view setNeedsLayout];
        }];
    }
}

如果您只想向上/向下轻扫并显示面板,您可以像这样使用 UISwipeGestureRecognizer:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // iVar
    swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipe:)];
    swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
    [self.view addGestureRecognizer:swipeUp];

    // Do the same again with swipeDown using UISwipeGestureRecognizerDirectionDown...
}

- (void)didSwipe:(UIGestureRecognizer *)swipe
{
    if (swipe == swipeUp) {
        // Show panel (see above)...
    } else {
        // Hide panel (see above)...
    }
}

如果您希望面板像打开控制中心一样跟踪您的手指,那么您可以使用 UIPanGestureRecognizer 并获取 translationInView:velocityInView: 并相应地调整面板。这是一段跟踪手指移动的代码片段,但使用了 touchesBegan:withEvent: - (void)touchesMoved:withEvent:- (void)touchesEnded:withEvent : UIViewController 中的方法让您体验一下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Don't worry too much about buttonView this is another view that I animate upwards to get out the way of the panel as it slides in from the left...
    [super touchesBegan:touches withEvent:event];
    CGPoint loc = [[touches anyObject] locationInView:self.view];
    // Save last touch for reference...
    lastTouch = loc;
    // leftBeginRect is an area where the user can start to drag the panel...
    // trackFinger defines whether the panel should move with the users gestures or not...
    if (CGRectContainsPoint(leftBeginRect, loc) && canTrack) {
        trackFinger = YES;
    }
    // Left view is a reference to the panel...
    else if (leftView.frame.size.width >= 300) {
        // This means that the panel is shown and therefore should track the user's finger back towards the edge of the screen...
        CGRect frame = CGRectMake(250, 0, 100, self.view.frame.size.height);
        if (CGRectContainsPoint(frame, loc) && canTrack) {
            trackFinger = YES;
        }
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    CGPoint loc = [[touches anyObject] locationInView:self.view];
    // Need to work out the direction in which the user is panning...
    if (lastTouch.x > loc.x) {
        currentFingerDirection = RHFingerDirectionLeft;
    }
    else {
        currentFingerDirection = RHFingerDirectionRight;
    }
    lastTouch = loc;
    if (trackFinger) {
        if (loc.x <= 300) {
            // This means that the panel is somewhere between fully exposed and closed...
            // This is where the frame for the left view (and the constraints) are adjusted according to the user's current finger position...
            CGRect frame = leftView.frame;
            frame.size.width = loc.x;
            [leftView setFrame:frame];
            leftViewConstraint.constant = loc.x;
            if (loc.x <= 80) {
                float percentage = loc.x / 80;
                int amount = 100 * percentage;
                CGRect otherFrame = buttonView.frame;
                otherFrame.origin.y = -amount;
                [buttonView setFrame:otherFrame];
                constraint.constant = constraintConstant + amount;
            }
        }
        else {
            CGRect frame = leftView.frame;
            frame.size.width = 300;
            [leftView setFrame:frame];
            leftViewConstraint.constant = 300;
            frame = buttonView.frame;
            frame.origin.y = -100;
            [buttonView setFrame:frame];
            constraint.constant = constraintConstant + 100;
            trackFinger = NO;
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    // This method works out if the panel should pop open or spring closed when the user ends the gesture...
    [super touchesEnded:touches withEvent:event];
    if (trackFinger) {
        CGPoint loc = [[touches anyObject] locationInView:self.view];
        if (loc.x >= 50 && currentFingerDirection == RHFingerDirectionRight) {
            CGRect frame = leftView.frame;
            frame.size.width = 300;
            leftViewConstraint.constant = 300;
            CGRect otherFrame = buttonView.frame;
            otherFrame.origin.y = -100;
            constraint.constant = constraintConstant + 100;
            [UIView animateWithDuration:0.2 animations:^{
                [leftView setFrame:frame];
                [buttonView setFrame:otherFrame];
            }];
        }
        else if (loc.x <= 250 && currentFingerDirection == RHFingerDirectionLeft) {
            CGRect frame = leftView.frame;
            frame.size.width = 0;
            leftViewConstraint.constant = 0;
            CGRect otherFrame = buttonView.frame;
            otherFrame.origin.y = 0;
            constraint.constant = constraintConstant;
            [UIView animateWithDuration:0.2 animations:^{
                [leftView setFrame:frame];
                [buttonView setFrame:otherFrame];
            }];
        }
        else if (loc.x <= 150) {
            CGRect frame = leftView.frame;
            frame.size.width = 0;
            leftViewConstraint.constant = 0;
            CGRect otherFrame = buttonView.frame;
            otherFrame.origin.y = 0;
            constraint.constant = constraintConstant;
            [UIView animateWithDuration:0.2 animations:^{
                [leftView setFrame:frame];
                [buttonView setFrame:otherFrame];
            }];
        }
        else {
            CGRect frame = leftView.frame;
            frame.size.width = 300;
            leftViewConstraint.constant = 300;
            CGRect otherFrame = buttonView.frame;
            otherFrame.origin.y = -100;
            constraint.constant = constraintConstant + 100;
            [UIView animateWithDuration:0.2 animations:^{
                [leftView setFrame:frame];
                [buttonView setFrame:otherFrame];
            }];
        }
        trackFinger = NO;
    }
    currentFingerDirection = RHFingerDirectionNone;
}

代码非常复杂,但它会产生一个漂亮的面板动画,它会像控制中心一样跟随您的手指。

关于ios - 如何制作像谷歌地图应用程序那样的向上滑动面板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28911319/

相关文章:

ios - 使用 Alamofire 4.0 进行参数编码时出现问题

ios - 如何使用CIContext drawImage :inRect:fromRect: with a CADisplayLink

objective-c - Objective-C 中的属性和实例变量

ios - 将 AVAsset 读入帧并编译回视频

ios - 从 UITableViewCell 转入如何包含 indexPath 数据?

ios - Core Data 数据库中的 Z_PK 列

ios - 是否有 iOS PWA 与蓝牙设备通信的解决方法?

ios - 在 native iOS 中使用 NSKeyedArchiver 序列化的 Unity 中反序列化 ARWorldMap

objective-c - MKMapView addAnnotation 未立即添加注释

ios - 完善我的 MagicalRecord 导入