How to replicate Messages bouncing bubbles in iOS

2019-01-22 04:35发布

问题:

When you are in a conversation within the Messages app in iOS 7, if you scroll up or down you will notice that the bubbles and more so the text saying when the messages were sent, will bounce into place.

I am trying to replicate this in my own table view, but am not finding a way to do it.

I assume it is using UIDynamics, but I am not sure how to tie that in with the scrolling and the content bouncing.

Any help would be appreciated

回答1:

If you want to replicate that effect yourself, you need UIKit Dynamics. I was interested in that effect myself and it was explained at WWDC this year.

Take a look at WWDC Session 217: Exploring Scroll Views on iOS 7

There are also read to use components on the internet, such as THSpringyCollectionView



回答2:

I was interested in that effect also and during my research on the web I found this tutorial - http://www.objc.io/issue-5/collection-views-and-uidynamics.html It is implementing the same idea.



回答3:

You can add a springy bounce to content in your scrollview like this:

  1. Set up a UIScrollview and add to your view.

    mainScroller = [UIScrollView new];
    mainScroller.frame = CGRectMake(0, 0, w, h);
    mainScroller.bounces = true;
    mainScroller.pagingEnabled = false;
    mainScroller.delegate = self;
    [self.view addSubview:mainScroller];
    
  2. Layout a UIView and add it within your scrollview.

    contentView = [UIView new];
    contentView.frame = CGRectZero;
    [mainScroller addSubview:contentView];
    
  3. Add subviews your your 'contentView'.

    UIView * unitView = [UIView new];
    unitView.frame = CGRectMake(0, yOff, w, 280);
    [contentView addSubview:unitView]; 
    
  4. Resize both contentView and scrollview to fit the content. Below w is view width and yOff the total cumulative height of the content.

    contentView.frame = CGRectMake(0, 0, w, yOff);
    mainScroller.contentSize = CGSizeMake(w, yOff);
    
  5. In your scrollViewDidScroll method, add the following code:

     float y = mainScroller.contentOffset.y;
     contentView.transform = CGAffineTransformMakeTranslation(0, y);
    
     for (UIView * sub in contentView.subviews){
         int index = (int)[contentView.subviews indexOfObject:sub];
         float delay = index * 0.03;
         float duration = 1.0f - delay;
    
         [UIView animateWithDuration:duration
                               delay:delay
              usingSpringWithDamping:0.8
               initialSpringVelocity:0.7
                             options:UIViewAnimationOptionCurveEaseInOut| UIViewAnimationOptionAllowUserInteraction
                          animations:^{
                              sub.transform = CGAffineTransformMakeTranslation(0, -y);
                         }
                     completion:^(BOOL finished){
                     }]; 
     }
    

The UIViewAnimationOptionAllowUserInteraction animation option allows for you to interact with the content immediately. Tweak the delay, duration, spring dampening and spring velocity variables to suit your needs.

The code could be further adapted to allow for touch detection; as it stands, the springy bounce originates at the top of the scrollView and descends down through the views, which for shorter scrollViews is barely noticeable.

EDIT: Touch detection

If you want to allow for touch detection, replace with these lines in your scrollViewDidScroll method:

int index = (int)[contentView.subviews indexOfObject:sub];
int deviation = abs(touchIndex - index);
float delay = deviation * 0.03;
float duration = 1.0f - delay;

Where the new variable touchIndex is defined like so:

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    //grab the location of the touch event
    CGPoint location = [scrollView.panGestureRecognizer locationInView:scrollView];
    int yTouch = location.y; //grab y coordinate
    touchIndex = (yTouch - 100) / 100; //calculate the index of the cell, where 100 is the height of the cell

}

If you have dynamic cell heights, store them in an array e.g. 'selectedHeights'. Then update your scrollViewWillBeginDragging method for the below, where the 'tally' float is set to 70 to allow for a header.

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    //grab the location of the touch event
    CGPoint location = [scrollView.panGestureRecognizer locationInView:scrollView];
    int yTouch = location.y; //grab y coordinate
    float tally = 70.0f;
    for (int n = 0; n < selectedHeights.count; n++){
        float cellHeight = [selectedHeights[n] floatValue];
        tally += cellHeight;
        if (tally > yTouch){
            touchIndex = n;
            break;
        }
    }
}