IOS UI篇-仿微博下拉刷新和上拉加載

前言

  • 關于下拉刷新和上拉加載,以前開發(fā)都是直接使用第三方MJRefresh,1-2句代碼集成,使用方便,省事;最近項目上架后比較閑,就研究了一下MJ,搜集了一些資料,自己模仿微博,寫了一個刷新控件。

效果圖

效果圖.gif

應用知識點

  • kvo

加載進父視圖時,注冊觀察者,實時監(jiān)聽UIScrollView的contentOffset的變化,根據(jù)變化展示不一樣的刷新狀態(tài),執(zhí)行相應的操作。

pragma mark -加入父視圖時添加觀察者
- (void)willMoveToSuperview:(UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];
    if (newSuperview) {
        self.superScrollview = (UIScrollView *)newSuperview;
        [self.superScrollview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
    }else {
        if (self.superScrollview) {
            [self.superScrollview removeObserver:self forKeyPath:@"contentOffset"];
            
        }
    }
}
#pragma mark-KVO的代理方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"]) {
  
        if (self.superScrollview.contentInset.top==64) {
            
            self.contentOffSetY=self.superScrollview.contentInset.top;
        }
        CGFloat y = self.superScrollview.contentOffset.y;
        if (self.superScrollview.isDragging) {
            //正在拖動
            
            if (y<  -self.contentOffSetY &&y> -self.contentOffSetY - headerRefeshHight && self.currentState ==TBStatuePulling) {
                //下拉狀態(tài)->正常狀態(tài)
                self.currentState = TBStatueNomal;
                
            }else if (y <= -self.contentOffSetY - headerRefeshHight && self.currentState == TBStatueNomal)
                //正常狀態(tài)->下拉狀態(tài)
            {
                self.currentState = TBStatuePulling;
            }
        }else if(self.currentState ==TBStatuePulling &&y <= -self.contentOffSetY - headerRefeshHight){ //拖拽釋放
            self.currentState = TBStatueRefreshing;
        }

    }
}
  • RunTime
    Runtime 很強大,需要研究的地方也很多,這里就不多講了,這里主要用到的關聯(lián)對象。
    首先來說,關聯(lián)對象,我們需要將tableView添加下拉刷新和下拉加載,系統(tǒng)并沒有這個屬性,我們需要寫個UISCrollView的類別,但我們知道Category只能添加方法,不能添加實例變量,如果只是添加屬性,其實是添加的setter和getter方法,不會自動生成實例變量的。
    這時候Runtime就發(fā)揮它的作用了
.h文件
@class TBRefreshHeadView;

@class TBRefreshFootView;

@interface UIScrollView (TBRefresh)
//下拉刷新
@property(nonatomic,weak)TBRefreshHeadView *header;
//上拉加載
@property(nonatomic,weak)TBRefreshFootView *footer;

//添加下拉刷新方法
-(void)addRefreshHeaderWithBlock:(void (^)())Block;

//添加上拉刷新方法
-(void)addRefreshFootWithBlock:(void (^)())Block;
.m文件
#pragma mark-關聯(lián)頭部
-(void)setHeader:(TBRefreshHeadView *)header
{
    
   objc_setAssociatedObject(self, @selector(header), header, OBJC_ASSOCIATION_ASSIGN);
    
}

-(TBRefreshHeadView*)header
{
     return objc_getAssociatedObject(self, @selector(header));
}


#pragma mark-關聯(lián)底部
-(void)setFooter:(TBRefreshFootView *)footer
{
    
    objc_setAssociatedObject(self, @selector(footer), footer, OBJC_ASSOCIATION_ASSIGN);
    
}

-(TBRefreshFootView*)footer
{
    
    return objc_getAssociatedObject(self, @selector(footer));
    
}

#pragma mark-初始化頭部
-(void)addRefreshHeaderWithBlock:(void (^)())Block
{
    TBRefreshHeadView *TBheader=[TBRefreshHeadView new];
    
    TBheader.ReturnBlock=Block;
    
    self.header=TBheader;
    
    [self insertSubview:TBheader atIndex:0];
    
}

#pragma mark-初始化底部
-(void)addRefreshFootWithBlock:(void (^)())Block
{
    
    TBRefreshFootView *TBfooter=[TBRefreshFootView new];
    
    TBfooter.ReturnBlock=Block;
    
    self.footer=TBfooter;
    
    [self insertSubview:TBfooter atIndex:0];

}

調(diào)用

刷新頭

//開始下拉刷新
-(void)beginRefreshing;

//結(jié)束下拉刷新
-(void)endHeadRefresh;

加載底部

//結(jié)束下拉加載
- (void)endFooterRefreshing;

//沒有更多數(shù)據(jù)
-(void)NoMoreData;

//將沒有更多數(shù)據(jù)狀態(tài)設置為正常狀態(tài)
-(void)ResetNomoreData;

使用

-(UITableView*)mainTableview
{
    if (!_mainTableview) {
    
        __weak ViewController *weakself=self;
        _mainTableview=[[UITableView alloc]initWithFrame:CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
        
        _mainTableview.delegate=self;
        
        _mainTableview.dataSource=self;
        
        [_mainTableview addRefreshHeaderWithBlock:^{
          
            [weakself LoadDatas];
            
        }];
        
        [_mainTableview addRefreshFootWithBlock:^{
          
            [weakself LoadMoreDatas];
        }];
   
    }
    return _mainTableview;
}

注意:移除觀察者

移除觀察者的時候,發(fā)現(xiàn)tableview是先于刷新頭和刷新尾釋放的,所以在tableView調(diào)用dealloc方法時,先將移除觀察者,然后將刷新頭和刷新尾置nil,發(fā)現(xiàn)dealloc方法會調(diào)用2次,查了資料發(fā)現(xiàn)。一次是 scrollView(實際tableView),另一次是UITableViewWrapperView,這個類是我們直接使用的。這種情況會出現(xiàn)在tableView上。scrollView 和 collectionView 不會出現(xiàn),所以我做了以下處理:

#pragma mark-測試可得,tableView先釋放,所以在釋放之前把頭部和尾部釋放,移除觀察者,不然會奔潰
-(void)dealloc
{
    if (self.header) {
        [self removeObserver:self.header forKeyPath:@"contentOffset"];
        self.header=nil;
    }
    
    if (self.footer) {
        [self removeObserver:self.footer forKeyPath:@"contentOffset"];
        
        self.footer=nil;
    }
}

最后

這個我第一篇簡書,寫的不好,大家多多指教!
附上github地址demo地址,ios 路還長,學習的很多,以后會繼續(xù)下去!
參考資料:

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,419評論 4 61
  • 任何時候都不要放棄夢想 盡管你可能走得慢 甚至可以算爬行 但一直走的很好、很安穩(wěn) 這就夠了!
    呃呃0618閱讀 278評論 0 0
  • Sublime Text 標簽(空格分隔): Tools Sublime Text 中關閉記住上次打開的文件 Ct...
    BlackNeko閱讀 326評論 0 0
  • 一家業(yè)績慘淡的公司值不值得留下來? 這正是我目前遇到的一個問題。公司業(yè)務形式轉(zhuǎn)型,身邊絕大多數(shù)同事都是業(yè)績平平,收...
    何武器丶閱讀 197評論 0 0
  • 兒子喜歡聽故事,每晚睡前總纏著我講故事。小孩子記憶力真是超人,有時候讀過的故事書下次我憑記憶講給他聽,他就...
    幸運草的天空閱讀 205評論 0 1

友情鏈接更多精彩內(nèi)容