UITableView with UIRefreshControl under a semi-tra

2019-03-28 09:23发布

问题:

I've created a UITableView which I want to scroll underneath my semi-transparent black status bar. In my XIB, I just set the table view's y position to -20 and it all looks fine.

Now, I've just added a pull-to-refresh iOS6 UIRefreshControl which works however, because of the -20 y position, it drags from behind the status bar. I'd like it's "stretched to" position to be under the status bar rather than behind.

It makes sense why it's messing up but there doesn't seem to be any difference changing it's frame and the tableview's content insets etc don't make a difference.

The docs suggest that once the refreshControl has been set, the UITableViewController takes care of it's position from then on.

Any ideas?

回答1:

You can subclass the UIRefreshControl and implement layoutSubviews like so:

@implementation RefreshControl {
    CGFloat topContentInset;
    BOOL topContentInsetSaved;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    // getting containing scrollView
    UIScrollView *scrollView = (UIScrollView *)self.superview;

    // saving present top contentInset, because it can be changed by refresh control
    if (!topContentInsetSaved) {
        topContentInset = scrollView.contentInset.top;
        topContentInsetSaved = YES;
    }

    // saving own frame, that will be modified
    CGRect newFrame = self.frame;

    // if refresh control is fully or partially behind UINavigationBar
    if (scrollView.contentOffset.y + topContentInset > -newFrame.size.height) {
        // moving it with the rest of the content
        newFrame.origin.y = -newFrame.size.height;

    // if refresh control fully appeared
    } else {
        // keeping it at the same place
        newFrame.origin.y = scrollView.contentOffset.y + topContentInset;
    }

    // applying new frame to the refresh control
    self.frame = newFrame;
}

It takes tableView's contentInset into account, but you can change topContentInset variable to whatever value you need and it will handle the rest.

I hope the code is documented enough to understand how it works.



回答2:

Just subclass the UIRefreshControl and override layoutSubviews like this:

- (void)layoutSubviews
{
    UIScrollView* parentScrollView = (UIScrollView*)[self superview];

    CGSize viewSize = parentScrollView.frame.size;

    if (parentScrollView.contentInset.top + parentScrollView.contentOffset.y == 0 && !self.refreshing) {
        self.hidden = YES;
    } else {
        self.hidden = NO;
    }

    CGFloat y = parentScrollView.contentOffset.y + parentScrollView.scrollIndicatorInsets.top + 20;

    self.frame = CGRectMake(0, y, viewSize.width, viewSize.height);

    [super layoutSubviews];
}


回答3:

The current upvoted answer does not play well with the fact you pull the component down (as Anthony Dmitriyev pointed out), the offset is incorrect. The last part is to fix it.

Either way: subclass the UIRefreshControl with the following method:

- (void)layoutSubviews
{
    UIScrollView* parentScrollView = (UIScrollView*)[self superview];
    CGFloat extraOffset = parentScrollView.contentInset.top;

    CGSize viewSize = parentScrollView.frame.size;

    if (parentScrollView.contentInset.top + parentScrollView.contentOffset.y == 0 && !self.refreshing) {
        self.hidden = YES;
    } else {
        self.hidden = NO;
    }

    CGFloat y = parentScrollView.contentOffset.y + parentScrollView.scrollIndicatorInsets.top + extraOffset;

    if(y > -60 && !self.isRefreshing){
        y = -60;
    }else if(self.isRefreshing && y <30)
    {
        y = y-60;
    }
    else if(self.isRefreshing && y >=30)
    {
        y = (y-30) -y;
    }

    self.frame = CGRectMake(0, y, viewSize.width, viewSize.height);

    [super layoutSubviews];
}


回答4:

UIRefreshControl always sits above the content in your UITableView. If you need to alter where the refreshControl is place, try altering the tableView's top contentInset. The UIRefreshControl takes that into account when determining where it should be positioned.



回答5:

Try this:

CGFloat offset = 44;
for (UIView *subview in [self subviews]) {
    if ([subview isKindOfClass:NSClassFromString(@"_UIRefreshControlDefaultContentView")]) {
        NSLog(@"Setting offset!");
        [subview setFrame:CGRectMake(subview.frame.origin.x, subview.frame.origin.y + offset, subview.frame.size.width, subview.frame.size.height)];
    }
}

This will move UIRefreshControll down for 44 points;



回答6:

I found the overriding of the layoutsubviews in the other answers did more than change the frame, they also change the behaviour (the control started sliding down with the content). To change the frame but not the behaviour I did this:

- (void)layoutSubviews {
    [super layoutSubviews];

    CGRect frame = self.frame;
    CGFloat desiredYOffset = 50.0f;
    self.frame = CGRectMake(frame.origin.x, frame.origin.y + desiredYOffset, frame.size.width, frame.size.height);

}