SVPullToRefresh
最近把項目里面的MJRefresh
換成了SVPullToRefresh
, 不得不說的是,SVPullToRefresh
是我見過最好用最省事的一個刷新控件,添加上拉刷新和下拉加載就一句話。
添加上拉刷新
- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler;
添加下拉加載
- (void)addInfiniteScrollingWithActionHandler:(void (^)(void))actionHandler;
下面說說在用它的時候,主要遇到的問題。
1.內容本地化,在類UIScrollView+SVPullToRefresh.m
中,都是用的NSLocalizedString
顯示的提示語,例如,pulltofresh...
,loading...
, 我們可以通過invoke - (void)setTitle:(NSString *)title forState:(SVPullToRefreshState)state;
這個函數,去改變它在不同的狀態顯示不同的中文,例如加載中..., 下拉刷新..., 釋放即可更新, 其實大可不必這么麻煩, 不然在每個用到它的地方都需要寫上三句話,你需要做的就是創建一個Localizable.strings
的類,將用到的提示語進行本地化,盡管你是用cocoapod 第三方導入的,這種方式依然是可以讓它 的內容本地化。
2.在 UIViewControl
視圖中添加sv 刷新控件,在viewDidload()
方法里面添加addpulltorefresh
, 整個視圖會往上偏移64像素。解決方式:
: 1.如果是在viewDidload
中調用 addPullToRefreshWithActionHandler
方法, 并且當前的類繼承UITableViewControl
或者UICollectionViewControl
,可以設置self.tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
,這樣設置后,解決了SVPullToRefresh
偏移像素的問題,并且導航欄還是透明的效果還是在的,因為此時self.edgesForExtendedLayout
依舊是默認的UIRectEdgeAll
(這里說的都是針對iOS 7+) ,所以self.tableview.frame
還是整個屏幕,包括了狀態欄和導航欄,所以現在的導航欄還會透明,就是這個原因了。
: 2.將當前的self.edgesForExtendedLayout
設為UIRectEdgeNone
,也就是當前的self.tableview.frame
是從導航欄下面開始算起的,這時你的內容根本都不會滑動到導航欄下面去,就別說什么導航欄透明的效果了。
edgesForExtendedLayout
可以看看它的官方文檔。這種方式只是解決因為添加刷新控件帶來的偏移問題,為了解決問題而解決問題。
探其究竟,和正確的解決方式。
如果你當前的視圖是UICollectionViewControl
或者UITableViewControl
,在沒有添加addPull...
之前,整個視圖都是正常的,self.tableView
能顯示在正確的位置,edgesForExtendedLayout
是默認的UIRectEdgeAll
,一切正常,而自從你在viewDidLoad
里 面,添加了如下代碼
- (void)viewDidLoad {
[super viewDidLoad];
[self setupDataSource];
__weak SVViewController *weakSelf = self;
// setup pull-to-refresh
[self.tableView addPullToRefreshWithActionHandler:^{
[weakSelf insertRowAtTop];
}];
// setup infinite scrolling
[self.tableView addInfiniteScrollingWithActionHandler:^{
[weakSelf insertRowAtBottom];
}];
}
這時候的tableview
會出現偏移問題,跑到SVPullToRefresh
源代碼中看究竟,然后發現 ,SVPullToRefreshView
的源點是以調用它的控件的源點設置的,
- (void)addPullToRefreshWithActionHandler:(void (^)(void))actionHandler position:(SVPullToRefreshPosition)position {
if(!self.pullToRefreshView) {
CGFloat yOrigin;
switch (position) {
case SVPullToRefreshPositionTop:
yOrigin = -SVPullToRefreshViewHeight;
break;
case SVPullToRefreshPositionBottom:
yOrigin = self.contentSize.height;
break;
default:
return;
}
SVPullToRefreshView *view = [[SVPullToRefreshView alloc] initWithFrame:CGRectMake(0, yOrigin, self.bounds.size.width, SVPullToRefreshViewHeight)];
view.pullToRefreshActionHandler = actionHandler;
view.scrollView = self;
[self addSubview:view];
//pulltorefreshView.originalTopInset` 用的是調用它視圖的contentInset.top
view.originalTopInset = self.contentInset.top;
view.originalBottomInset = self.contentInset.bottom;
view.position = position;
self.pullToRefreshView = view;
self.showsPullToRefresh = YES;
}
}
問題出現了,在viewDidload
里面,self.tableView
或者是 self.collectionView
的contentInset
為(0,0,0,0); 而實際上是,如果是用的 UITableViewControl
,最后顯示的self.tableview
的contentOffset: {0, -64}
, 也就是說,其實是系統是會在再重設下self.tableView
的布局再去顯示的。而我們在viewDidload
時去添加刷新控件,而這時的tableview
的布局并沒有完全的確定,所以會往上偏移。所以解決這類問題最好的方法也就是,等視圖上控件的 frame
完全被設置好后,再去添加SVPullToRefresh
控件,換言之,我們只需要將viewDidload()
里面添加的刷新方法移到 viewDidLayoutSubviews
中,這時的tableView frame
已被設置好,自然控件的地方也會顯示對。
- (void)viewDidLayoutSubviews {
if (self.tableView.pullToRefreshView == nil) {
[self.tableView addPullToRefreshWithActionHandler:^{
}];
}
}
最后, 以UITableViewContro
為例,看看它 的生命周期,以及tableView
frame
和contentInset
的變化,并更加深層次的促進對viewcontrol
的生命周期的認識
2015-10-30 00:27:48.699 LifeCycle[1197:84273] viewDidLoad = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, 0}; contentSize: {600, 0}>
2015-10-30 00:27:48.720 LifeCycle[1197:84273] viewWillAppear = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {600, 0}>
2015-10-30 00:27:48.733 LifeCycle[1197:84273] viewWillLayoutSubviews = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {414, 0}>
2015-10-30 00:27:48.778 LifeCycle[1197:84273] viewDidLayoutSubviews = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {414, 0}>
2015-10-30 00:27:48.779 LifeCycle[1197:84273] viewWillLayoutSubviews = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {414, 0}>
2015-10-30 00:27:48.824 LifeCycle[1197:84273] viewDidLayoutSubviews = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {414, 0}>
2015-10-30 00:27:48.831 LifeCycle[1197:84273] viewDidAppear = <UITableView: 0x7fa9e5898000; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fa9e3fd3c10>; layer = <CALayer: 0x7fa9e3f7d730>; contentOffset: {0, -64}; contentSize: {414, 0}>
從上面,并不難看出,在viewWillLayoutSubviews
方法中, 系統對tableView
的contentOffset
和contentSize
進行了調整。所以,一般用到tableView
布局關系的,最好是在它布局穩定下后再去進行調整或添加。
再后來項目中發現,使用SVPullToRefresh 會在iOS 7 上crash, 經過層層調試,最后解決方法竟然需要改sv 的源代碼,添加