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

时间:2015-03-07 04:17:24

标签: ios objective-c

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

我只想在现有的视图控制器中添加子视图。我该怎么做呢?

2 个答案:

答案 0 :(得分:6)

我在我的iOS应用程序中使用了向上滑动面板,我发现诀窍是将自定义视图添加到故事板(或xib文件)中的视图控制器,但设置其框架以便它不在屏幕上。您可以使用layout constraints确保视图在任何设备上都不在屏幕上。

然后,它只是在适当的时候在屏幕上设置动画的动画。 e.g:

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

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

答案 1 :(得分:3)

很抱歉迟到了回复。我根据Rob的答案创建了一个小型的lib。 https://github.com/hoomazoid/CTSlidingUpPanel

使用起来非常简单:

@IBOutlet weak var bottomView: UIView!
var bottomController:CTBottomSlideController?;


override func viewDidLoad() {
    super.viewDidLoad()
    //You can provide nil to tabController and navigationController
    bottomController = CTBottomSlideController(parent: view, bottomView: bottomView, 
                    tabController: self.tabBarController!,
                    navController: self.navigationController, visibleHeight: 64)
    //0 is bottom and 1 is top. 0.5 would be center                
    bottomController?.setAnchorPoint(anchor: 0.7)
}