如何在iOS 7中复制消息反弹气泡

15

在iOS 7中使用Messages应用程序进行对话时,如果您向上或向下滚动,您会注意到气泡以及更多的文字会反弹回原位。

我正在尝试在自己的表视图中复制这个效果,但是找不到方法。

我猜它是使用UIDynamics,但我不确定如何将其与滚动和内容反弹联系起来。

任何帮助将不胜感激


我知道答案,可以搜索Brandon Alexander和Black Pixel的BPXLFlowLayout。他的类BPXLFlowLayout非常接近于Messages中物理效果的真实感觉。 - Fattie
顺便提一下,在处理这些东西时不要忘记这个关键提示:http://stackoverflow.com/a/23926712/294884 - Fattie
只为更加明确,本质答案是“您使用UICollectionViewFlowLayout”。就像我在上面提到的一样,BPXLFlowLayout是BPXLFlowLayout的一个惊人版本,完美地完成您想要的功能。 - Fattie
3个回答

12
如果你想自己复制那种效果,你需要使用UIKit Dynamics。我自己对那种效果很感兴趣,今年在WWDC上进行了解释。
请看WWDC会议217:探索iOS 7上的滚动视图。
互联网上也有可供使用的组件,例如THSpringyCollectionView

12

1
我发现这个教程(以及WWDC会议上的演示)并没有复制消息应用程序的行为。在消息应用程序中,没有振荡,一切都很顺畅。在演示应用程序中,即使您的阻尼比≥1(在物理上没有意义),仍然存在残余振荡。 - KPM
1
@KPM 尝试给弹簧附着行为设置一些长度(例如1.0f)。 - stephencelis
1
嗨,awolf!听起来很不错,你应该发布你的代码。让我们看看吧。我只是使用BPXL并调整了一些值就完美地使它工作了,但我们可能在处理不同大小的东西或其他什么问题?干杯! - Fattie
1
@ChaseS 这是为客户项目而做的,因此不能简单地发布代码。一般来说,我需要进行的主要调整是向负责弹簧动作的UIAttachmentBehavior添加一个操作块。每次UIAttachmentBehavior在UIDynamicAnimator中计算位置时,都会调用此操作块。 - awolf
3
@ChaseS 在那个代码块里,我会查看弹簧锚点和物品点之间的差值(将其称为变量“d”)。如果d小于1.0,我会简单地应用一个极大的阻尼系数,如2000.0(以便它能立即稳定下来)。否则,我会使用二次函数确定阻尼系数(例如[常数] * d^2 + [另一个常数])。我是通过实验选择[常数]和[另一个常数]的。最后,我还调整了行为的频率,对于较小的d值选择略高的频率。祝你好运! - awolf
显示剩余6条评论

4

在此输入图片描述

您可以通过以下方式为滚动视图中的内容添加弹性效果:

  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){
                     }]; 
     }
    

UIViewAnimationOptionAllowUserInteraction动画选项允许您立即与内容交互。调整延迟、持续时间、弹簧阻尼和弹簧速度变量以适应您的需求。

代码可以进一步适应触摸检测;目前,弹性反弹从scrollView的顶部开始,并通过视图向下降落,对于较短的scrollViews几乎不可感知。

编辑:触摸检测

如果要允许触摸检测,请在scrollViewDidScroll方法中替换为以下行:

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

新变量touchIndex的定义如下:
-(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

}

如果您的单元格高度是动态的,请将它们存储在一个数组中,例如'selectedHeights'。然后更新您的scrollViewWillBeginDragging方法如下,其中“tally”浮点数设置为70,以允许有一个标题:

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

请问您能否解释一下如何在TableView中实现弹跳动画?我只需要弹跳效果。 - sujith1406
我想,使用tableview或者自定义流式布局的uicollectionview,可以追踪单元格相对于superview的位置,但我不知道具体实现方法,也许其他人知道?如果你担心在大量单元格上进行dequeing,我可以发布一些相关代码。整个过程所需的代码行数并不比苹果要求的tableview方法多,当你开始添加动态背景(如消息应用程序中的蓝色渐变)时,这是有意义的。 - Johnny Rockex
嗨@JohnnyRockex,你能否把你的代码放在一个演示中呢?就像你的gif展示的那样?这样会更有价值。谢谢。 - Paradise
@JohnnyRockex,你能解释一下表视图吗?或者请提供示例代码。 - Sai kumar Reddy
@SaikumarReddy 你好Sai,我已经有一段时间没有写这个代码了,但是看起来它并没有使用UITableView。现在我的猜测是要使用tableView或者UICollectionView,并且在scrollViewDidScroll方法中连接到函数,然后从那里进行操作。祝你好运。 - Johnny Rockex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接