如何创建一条短暂出现在 UITableView(如 UIRefreshControl)“上方”的状态消息?

Posted

技术标签:

【中文标题】如何创建一条短暂出现在 UITableView(如 UIRefreshControl)“上方”的状态消息?【英文标题】:How would I create a status message that briefly appears "above" a UITableView (like UIRefreshControl)? 【发布时间】:2013-05-27 12:26:59 【问题描述】:

我正在尝试用 UIView 来补充 UIRefreshControl,其中包含一条消息 (statusMessageView),该消息在应用启动后出现在 UITableViewController 的“上方”。此视图将通知用户UITableViewController 已刷新,无需刷新。

以下是我想要完成的任务的细分:

应用程序启动,UITableViewController 显示正常,然后向下滚动 44px 以显示statusMessageView(高度为 44px)。

statusMessageView 保持可见 2 秒

UITableViewController 动画滚动到它的原始位置,有效地“折叠”statusMessageView 离开。 (类似UIRefreshControl,但使用代码进行动画处理)

注意:这个statusMessageView会和一个UIRefreshControl一起使用,所以显示后需要“走开”,这样UIRefreshControl才能正常使用

我查看了其他第 3 方“拉动刷新”控制器,但我认为这是矫枉过正,因为我使用的是 UIRefreshControl

【问题讨论】:

在状态栏上显示状态感觉如何? cocoacontrols.com/controls/kgstatusbar 虽然它实现了通知用户的既定目标,但我希望将状态消息附加到 tableView - 它向用户提供了一个重要提示,即下拉 tableView 是一种选择,并且会导致采取行动。 【参考方案1】:

似乎这里的其他答案正在为下拉到 UINavigationBar 下方的通知提供解决方案。如果您仍在寻找位于 UITableView 的滚动视图中的解决方案,那么我会在表视图中添加一个自定义表头(而不是节头)。

以下是完成此操作所需的粗略步骤:

1.在加载时创建初始标题视图

我通常使用拥有 UITableView 实例变量的 UIViewController 子类(而不是使用 UITableViewController),但您应该能够通过任一设置来完成此操作。在您的 tableview 设置代码中(可能在 viewDidLoad 中),您可以在其中设置诸如 backgroundColor、contentInset、separatorStyle 等内容,创建一个将成为您的标题的 UILabel。然后将此 UILabel 设置为您的 tableView 的 tableHeaderView。当然,如果你想让这个“通知部分”变得更复杂一些,请随意将其设置为带有嵌套 UILabel + 其他内容的 UIView。所以像:

UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView.bounds.size.width, 44.0f)];
headerLabel.backgroundColor = [UIColor clearColor]; // Just in case you have some fancy background/color in your table view
headerLabel.textAlignment = UITextAlignmentCenter;
headerLabel.font = ....
headerLabel.textColor = ....
headerLabel.text = @"You are an awesome user!";
self.tableView.tableHeaderView = headerLabel;

2。将您的表格视图设置为“正常”加载(即不显示标题)

再次,在 viewDidLoad 中,您需要正确设置 tableview 的 contentOffset 和 alwaysBounceVertical 以在加载时隐藏此标题视图。 contentOffset 设置为标题的高度将在标题下方开始 tableview 的 y 坐标。 alwaysBounceVertical 设置为 YES 将允许您的 tableview 正常运行,即使您的 tableview 的 contentsize 小于您的屏幕尺寸。所以像:

self.tableView.contentOffset = (CGPoint)0.0f, 44.0f;
self.tableView.alwaysBounceVertical = YES;

3.添加向下滑动并滑开

好的,现在有几种方法可以做到这一点。在 viewDidAppear 上,您可以创建一个链式 UIView 动画,其中第一个动画将 tableview 向下滑动(即将 contentOffset 设置为 0.0f, 0.0f)延迟一秒,第二个动画将 tableview 向上滑动(即设置contentOffset 到 0.0f, 44.0f) 延迟两秒。或者您可以使用 GCD 并将两个动画安排为异步 + 延迟块。无论哪种方式都很好(可能还有两到三种其他的好方法可以实现这一点),但只是为了传达这个想法......您可以像这样链接动画:

__weak MyCustomViewController *me = self;
[UIView 
    animateWithDuration:0.4f 
    delay:1.0f 
    options:UIViewAnimationOptionAllowUserInteraction 
    animations:^
     
        me.tableView.contentOffset = (CGPoint)0.0f, 0.0f; 
     
    completion:^(BOOL finished)
     
        if (me.tableView) 
         
            [UIView 
                animateWithDuration:0.4f 
                delay:2.0f 
                options:UIViewAnimationOptionAllowUserInteraction 
                animations:^
                 
                    me.tableView.contentOffset = (CGPoint)0.0f, 44.0f; 
                 
                completion:^(BOOL finished)
                 
                    if (me.tableView) 
                      
                        me.tableView.tableHeaderView = nil; // If you want to completely get rid of this notification header
                        me.tableView.contentOffset = (CGPoint)0.0f, 0.0f; // I'm unsure if this will cause the tableview to jump... if it does, you can animate the headerview away instead of animating the tableview up to hide the header, then setting header to nil and reseting the contentOffset

                        // or

                        me.tableView.tableHeaderView.hidden = YES; // If you want to just hide the header
                     
                ]; 
         
    ];

我编写了示例代码而没有对其进行任何测试...呵呵,所以它可能无法正常工作。但是,如果您需要更多帮助,我们很乐意帮助您充实这一点!祝你好运。

更新:允许用户滚动取消动画

我不太确定您希望桌子上的用户交互对动画做什么。如果您只想在用户开始滚动时取消动画,那么我会使用 GCD(参见下面的代码)。但我可以看到其他方法可以让动画与用户触摸一起工作,所以这取决于你在寻找什么。在任何情况下,假设任何用户触摸都应该禁用下一个预定动画,那么可以使用以下两个函数来实现:

- (void)scheduleShowHeaderAnimation

    __weak MyCustomViewController *me = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0f * NSEC_PER_SEC), dispatch_get_main_queue(), ^ // Start animating after 1 sec delay
    
        if (me.tableView) // Make sure the tableview is still around
        
            if (! me.tableView.tracking &&   // Don't show animation if user has begun to touch contentview
                ! me.tableView.dragging &&   // Don't show animation if user has begun to drag contentview
                ! me.tableView.decelerating) // Don't show animation if dragging happened and scrollview is starting to decelerate
            
                [UIView
                    animateWithDuration:0.4f
                    delay:0.0f
                    options:UIViewAnimationOptionAllowAnimatedContent // This will make sure user can interact with tableview while animation is going on
                    animations:^
                    
                        me.tableView.contentOffset = (CGPoint)0.0f, 0.0f;
                    
                    completion:^(BOOL finished)
                    
                        [me scheduleHideHeaderAnimation];
                    ];
            
        
    );


- (void)scheduleHideHeaderAnimation

    __weak MyCustomViewController *me = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0f * NSEC_PER_SEC), dispatch_get_main_queue(), ^ // Start animating after 2 secs delay
    
        if (me.tableView) // Make sure the tableview is still around
        
            if (! me.tableView.tracking &&   // Don't show animation if user has begun to touch contentview
                ! me.tableView.dragging &&   // Don't show animation if user has begun to drag contentview
                ! me.tableView.decelerating) // Don't show animation if dragging happened and scrollview is starting to decelerate
            
                [UIView
                    animateWithDuration:0.4f
                    delay:0.0f
                    options:UIViewAnimationOptionAllowAnimatedContent // This will make sure user can interact with tableview while animation is going on
                    animations:^
                    
                        me.tableView.contentOffset = (CGPoint)0.0f, 44.0f;
                    
                    completion:^(BOOL finished)
                    
                        if (me.tableView)
                        
                            me.tableView.tableHeaderView = nil; // If you want to completely get rid of this notification header
                            me.tableView.contentOffset = (CGPoint)0.0f, 0.0f; // I'm unsure if this will cause the tableview to jump... if it does, you can animate the headerview away instead of animating the tableview up to hide the header, then setting header to nil and reseting the contentOffset

                            // or

                            me.tableView.tableHeaderView.hidden = YES; // If you want to just hide the header
                        
                    ];
            
        
    );

我会在 viewDidAppear 中调用 scheduleShowHeaderAnimation。

然后为了支持在用户已经向下滚动 tableview 时隐藏标题,我将实现 UIScrollViewDelegate 的 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 并添加如下内容:

if (self.tableView.tableHeaderView != nil)

    self.tableView.tableHeaderView = nil;


// or

if (self.tableView.tableHeaderView.hidden == NO)

    self.tableView.tableHeaderView.hidden = YES;

如果您想要支持更复杂的交互,或者如果您希望动画以不同的方式响应用户触摸,您可能需要覆盖其他 UIScrollViewDelegate 方法以及当用户开始与滚动视图交互时(即表视图的父类),然后更改动画的行为。

这会让你更接近你正在寻找的东西吗?

【讨论】:

没有任何用户交互,这确实符合我的要求,但是如果用户在动画之前或期间开始滚动,它会产生不良结果。您可以修改代码以适应这种情况吗?我不想简单地禁用滚动视图,tableView 需要在显示后立即响应触摸。 (不过,你已经很接近了):) 用一种可能的实现方式更新了答案以支持用户滚动。如果 tableview 需要对用户的触摸做出不同的响应,则需要做更多的工作。 看起来不错,我今天下午测试一下! 郑重声明,我授予您代表,因为您交付的正是我所要求的。但最后我最终使用 MBProgressHud 向用户显示同步通知,无论滚动位置在 UITableView 中的哪个位置。我得到了我想要的,我只是不知道我想要的不是我需要的。 :)【参考方案2】:

除了@Khawar 的回答,还有一个选择:https://github.com/toursprung/TSMessages

【讨论】:

【参考方案3】:

您可以为此目的使用以下库。您可以根据需要设置自动隐藏持续时间。您还可以自定义其外观。

https://github.com/tciuro/NoticeView

【讨论】:

【参考方案4】:

这可以通过一种非常简单的方式完成:您只需创建一个子视图并将其插入到您的刷新控件中:

- (void)viewDidLoad 
    [super viewDidLoad];
    ...
    UIView *smView = [self statusMessageView];
    [self.refreshControl insertSubview: smView atIndex: 0];

    // to show and hide the view inserted in refresh control
    // With this transparent loader color even the middle area is not covered
    // so you can have all the space to show your message
    self.refreshControl.tintColor = [UIColor colorWithWhite:1.0 alpha:0.0];
    [self.refreshControl beginRefreshing];

    [self afterDelay: 2.0  performBlock: ^()
        [self.refreshControl endRefreshing];
        // this delay is needed to prevent loader animation becoming visible
        // while the refresh control is being hidden
        [self afterDelay: 0.25  performBlock: ^()
            self.refreshControl.tintColor = nil;
        );
    );
    ...


-(void)afterDelay: (float)seconds  performBlock: (void (^)())block  
    dispatch_time_t popTime =
        dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), block);


-(UIView)statusMessageView 
    ...
    return view;

您需要的视图大小为 320x43 像素(如果它更高,则底部会在刷新控件隐藏的情况下可见),中间区域(大约 35 像素)将被加载器动画覆盖(但在您最初显示消息时不会覆盖) )。

我使用 UIImageView 在那里显示应用程序徽标(它更简单:参见here)。

我可能希望(作为用户)在开始拉取之前不要看到此消息。当我开始拉动时,我会看到在刷新开始之前不需要更新(当您下拉大约两倍于该子视图的高度时,它就会开始,因此用户将有时间查看那里有什么以及是否需要刷新)。如果表格中有未读项目(推文、帖子等),您可以在该视图中显示未读项目的数量。

但这是个人喜好问题,它的完成方式似乎以非常简单的方式实现了您想要的。我测试过,一切正常。

【讨论】:

以上是关于如何创建一条短暂出现在 UITableView(如 UIRefreshControl)“上方”的状态消息?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建一条可靠的实时数据流

如何创建一条可靠的实时数据流

IOS swift如何在tableView单元格内创建一条小水平线

如何在sql创建一条插入数据的存储过程

使用 CSS 在元素后面创建一条线:before

如何用Unity 和Vuforia 创建一款AR应用