如何创建一个简短地显示在UITableView(如UIRefreshControl)“上方”的状态消息?

时间:2013-05-27 12:27:00

标签: ios uitableview uirefreshcontrol

我正在尝试用UIRefreshControl补充UIView,其中statusMessageView包含在应用启动后显示在UITableViewController上方的消息(UITableViewController)。此视图将通知用户UITableViewController已刷新且无需刷新。

以下是我要完成的细分:

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

UITableViewController保持可见2秒

statusMessageView动画向上滚动到原始位置,有效地“掖”UIRefreshControl。 (例如statusMessageView,但使用代码进行动画处理)

注意:此UIRefreshControl将与UIRefreshControl一起使用,因此在显示后需要“消失”,以便{{1} }可以正常使用

我已经看过其他第三方“拉动刷新”控制器了,但我认为由于我使用的事实UIRefreshControl

,这样做太过分了

4 个答案:

答案 0 :(得分:2)

除了@Khawar的答案之外,还有另一种选择:
https://github.com/toursprung/TSMessages

答案 1 :(得分:2)

这里的其他答案似乎是为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。将您的tableview设置为“正常”加载(即不显示标题)

同样,在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})延迟一秒,第二个动画将桌面视图滑回(即设置) contentOffset为{0.0f,44.0f})延迟两秒。或者您可以使用GCD并将两个动画安排为async +延迟块。无论哪种方式都很好(并且可能还有两到三种其他好方法可以实现这一点),但只是为了实现这个想法...你可以像这样链接动画:

__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方法,并且当用户开始与滚动视图交互时(这是表视图的父类),然后更改动画的行为。

这会让你更接近你想要的吗?

答案 2 :(得分:0)

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

https://github.com/tciuro/NoticeView

答案 3 :(得分:0)

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

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

你需要的视图大小是320x43px(如果它更高,它的底部将隐藏刷新控件可见),中间区域(大约35px)将被加载器动画覆盖(但最初显示消息时不会)。

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

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

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