TNKRefreshControl 1.1.0

TNKRefreshControl 1.1.0

TestsTested
LangLanguage Obj-CObjective C
License MIT
ReleasedLast Release Mar 2017

Maintained by David Beck.




TNKRefreshControl is a replacement for UIRefreshControl that can be used with any UIScrollView and uses a more modern look.

Screenshot

Usage

To run the example project, clone the repo, and run pod install from the Example directory first.

See the documentation for more details.

Instead of setting refreshControl on a UITableViewController, you create and set a TNKRefreshControl on any UIScrollView or UIScrollView subclass like UITableView.

self.tableView.tnk_refreshControl = [TNKRefreshControl new];
[self.tableView.tnk_refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

From there, you can programatically activate the refresh control programatically with beginRefreshing. When you have finished loading content, make sure to call endRefreshing.

- (IBAction)refresh:(id)sender {
    [self.tableView.tnk_refreshControl beginRefreshing];

    [_objectSource loadNewObjects:^(NSArray *newDates) {
        [self.tableView.tnk_refreshControl endRefreshing];

        [self.tableView reloadData];
    }];
}

UITableView Floating Headers and updating from 0.6.0

UITableView has a nasty habit of placing it's section headers below contentInset. This causes floating section headers to appear lower than they should when the refresh control is active. Previously, TNKRefreshControl would use method swizzling to adjust the headers. However, for many user's of the framework, they are not using the refresh control with floating headers, but layoutSubviews gets swizzled on UITableView for everyone that includes the framework in their project.

If you still need the fix for floating headers, you can include this code in your project, along with JRSwizzle. Further, you must use -[UITableView dequeueReusableHeaderFooterViewWithIdentifier:] (or the default header views) for this to work. See rdar://19489536 for more info.

@implementation UITableView (TNKRefreshControl)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSError *error;
        BOOL result = [[self class] jr_swizzleMethod:@selector(layoutSubviews) withMethod:@selector(TNK_layoutSubviews) error:&error];
        if (!result || error) {
            NSLog(@"Can't swizzle methods - %@", [error description]);
        }
    });
}

- (void)TNK_layoutSubviews
{
    [self TNK_layoutSubviews]; // this will call layoutSubviews implementation, because we have exchanged them.


    // UITableView has a nasty habbit of placing it's section headers below contentInset
    // We aren't changing that behavior, just adjusting for the inset that we added

    if (self.tnk_refreshControl.addedContentInset.top != 0.0) {
        //http://b2cloud.com.au/tutorial/uitableview-section-header-positions/
        const NSUInteger numberOfSections = self.numberOfSections;
        const UIEdgeInsets contentInset = self.contentInset;
        const CGPoint contentOffset = self.contentOffset;

        const CGFloat sectionViewMinimumOriginY = contentOffset.y + contentInset.top - self.tnk_refreshControl.addedContentInset.top;

        //  Layout each header view
        for(NSUInteger section = 0; section < numberOfSections; section++)
        {
            UIView* sectionView = [self headerViewForSection:section];

            if(sectionView == nil)
                continue;

            const CGRect sectionFrame = [self rectForSection:section];

            CGRect sectionViewFrame = sectionView.frame;

            sectionViewFrame.origin.y = ((sectionFrame.origin.y < sectionViewMinimumOriginY) ? sectionViewMinimumOriginY : sectionFrame.origin.y);

            //  If it's not last section, manually 'stick' it to the below section if needed
            if(section < numberOfSections - 1)
            {
                const CGRect nextSectionFrame = [self rectForSection:section + 1];

                if(CGRectGetMaxY(sectionViewFrame) > CGRectGetMinY(nextSectionFrame))
                    sectionViewFrame.origin.y = nextSectionFrame.origin.y - sectionViewFrame.size.height;
            }

            [sectionView setFrame:sectionViewFrame];
        }
    }
}

@end

Author

David Beck, [email protected]

License

TNKRefreshControl is available under the MIT license. See the LICENSE file for more info.