1. 程式人生 > >iOS 自定義重新整理控制元件UIScrollView (Refresh)

iOS 自定義重新整理控制元件UIScrollView (Refresh)

前言:

開發的時候經常會用到下拉重新整理這個控制元件,一直以來想自己寫一個,但是時間問題,都是使用別人寫好的,今天查了資料,自己自定一個

1.主要原理

       a.建立UIScrollView的類目 提供 類似addHeaderRefresh等方法,這樣tableview collectview等他們都是整合UIScrollView,所以可以直接條用類目提供的方法,(很多第三方基本都是這麼幹) 一般上拉 下拉都是捕獲scrollview的偏移(contentoffset),但是在UIScrollview的類目裡面如果捕獲這個東西呢?我一開始是想通過類目裡面自己實現scrollview的delegate,根據- (void

)scrollViewDidScroll:(UIScrollView *)scrollView;   獲取contentoffset,但是這樣設計違背了設計理念:作為外掛而言。就破壞了別人原本的邏輯。比如別人也需要監聽這些行為。可能互相覆蓋什麼的。所以採用KVO的方式兼聽屬性contentoffset的變化 就很好的解決了這一點

        b.再建立一個自定義的展示樣式CustomRefreshView,這個view裡面根據scrollview的偏移配置對應的UI展示 (捕獲到“正在重新整理”的狀態的時候 利用外部傳進來的重新整理方法:SEL 和 執行方法的目標target 執行[self

.actionTargetperformSelector:self.actionwithObject:nilafterDelay:0];

2.只是儲備,類目的相關知識  物件關聯等

直接上程式碼:(UIScrollView+Refresh).h

//
//  UIScrollView+Refresh.h
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/30.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "CustomRefreshView.h"



@interface UIScrollView (Refresh)
@property (nonatomic,strong) CustomRefreshView *topShowView;

-(void)addHeaderRefreshWithTarget:(id)target action:(SEL)action;

-(void)beginHeaderRefresh;
-(void)endHeaderRefresh;

@end


.m

//
//  UIScrollView+Refresh.m
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/30.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import "UIScrollView+Refresh.h"
#import <objc/runtime.h>
#define kObservePath        @"contentOffset"

@implementation UIScrollView (Refresh)

static char topShowViewKey;

-(void)addHeaderRefreshWithTarget:(id)target action:(SEL)action
{
    if (!self.topShowView)
    {
        self.topShowView = [[CustomRefreshView alloc] init];
    }
    self.topShowView.frame = CGRectMake(0, -100, self.frame.size.width, 100);
    self.topShowView.parentView = self;
    self.topShowView.actionTarget = target;
    self.topShowView.action = action;
    [self addSubview:self.topShowView];
    //兼聽滾動便宜
    [self addObserver:self forKeyPath:kObservePath options:NSKeyValueObservingOptionNew context:nil];
}

-(void)beginHeaderRefresh
{
    [self.topShowView beginHeaderRefresh];
}
-(void)endHeaderRefresh
{
    [self.topShowView endHeaderRefresh];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if ([kObservePath isEqualToString:keyPath])
    {
        NSValue *point = (NSValue *)[change objectForKey:@"new"];
        CGPoint p = [point CGPointValue];
        [self.topShowView adjustY:-p.y];
    }
    
    
}
-(CustomRefreshView *)topShowView
{
    return objc_getAssociatedObject(self, &topShowViewKey);
}

-(void)setTopShowView:(CustomRefreshView *)topShowView
{
    objc_setAssociatedObject(self, &topShowViewKey, topShowView, OBJC_ASSOCIATION_RETAIN);
}
-(void)dealloc
{
    if (self.topShowView)//註冊過重新整理的必須要除移開監聽
    {
        [self removeObserver:self forKeyPath:kObservePath context:kObserveContent];
    }
}

@end


自定義展示的UI檢視:

CustomRefreshView.h

//
//  CustomRefreshView.h
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/30.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import <UIKit/UIKit.h>


typedef NS_ENUM(NSUInteger,RefreshStatus)
{
    RefreshStatus_Normal = 1,
    RefreshStatus_BeginRefresh,
    RefreshStatus_Refreshing,
};

@interface CustomRefreshView : UIView

@property (nonatomic,strong)UILabel *updateLabel;
@property (nonatomic,weak) id actionTarget;
@property (nonatomic)SEL action;
@property (nonatomic,strong) UIScrollView *parentView;
@property (nonatomic) RefreshStatus refreshStatus;

-(void)beginHeaderRefresh;
-(void)endHeaderRefresh;

-(void)adjustY:(CGFloat)y;



@end

.m

//
//  CustomRefreshView.m
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/30.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import "CustomRefreshView.h"
#define kMinOffSetY     100

@implementation CustomRefreshView

-(instancetype)init
{
    if (self = [super init])
    {
        [self addSubview:self.updateLabel];
        self.refreshStatus = RefreshStatus_Normal;
    }
    return self;
}

-(void)layoutSubviews
{
    self.updateLabel.frame = CGRectMake(0, self.frame.size.height - 20, self.frame.size.width, 20);
}
-(void)beginHeaderRefresh
{
    self.refreshStatus = RefreshStatus_Refreshing;
}
-(void)endHeaderRefresh
{
    [self isAdjustToNormal:YES];
    self.refreshStatus = RefreshStatus_Normal;
    
}

-(void)adjustY:(CGFloat)y
{
    if (self.parentView.isDragging)
    {
        if (y>kMinOffSetY)
        {
            self.refreshStatus = RefreshStatus_BeginRefresh;
        }
        else
        {
            self.refreshStatus = RefreshStatus_Normal;
        }
    }
    else
    {
        if (y>kMinOffSetY)
        {
            self.refreshStatus = RefreshStatus_Refreshing;
        }
    }
}

-(void)isAdjustToNormal:(BOOL)normal
{
    CGFloat y = 0;
    if (!normal)
    {
        y = 50;
    }
    __weak CustomRefreshView *weakSelf = self;
    [UIView animateWithDuration:0.5 animations:^{
        weakSelf.parentView.contentInset = UIEdgeInsetsMake(y, 0, 0, 0);
    }];
}
-(void)doNormalRefresh
{
    self.updateLabel.text = @"下拉重新整理....";
}
-(void)doBeginRefresh
{
    self.updateLabel.text = @"釋放載入....";
}
-(void)doRefreshing
{
    self.updateLabel.text = @"正在努力載入...";
    [self isAdjustToNormal:NO];
    [self.actionTarget performSelector:self.action withObject:nil afterDelay:0];
}



-(void)setRefreshStatus:(RefreshStatus)refreshStatus
{
    if(_refreshStatus == refreshStatus)
        return;
    
    switch (refreshStatus)
    {
        case RefreshStatus_Normal:
            [self doNormalRefresh];
            break;
        case RefreshStatus_BeginRefresh:
        {
            [self doBeginRefresh];
            break;
        }
        case RefreshStatus_Refreshing:
        {
            [self doRefreshing];
            break;
        }
        default:
            break;
    }
    _refreshStatus = refreshStatus;
}

-(UILabel *)updateLabel
{
    if (_updateLabel == nil)
    {
        _updateLabel = [[UILabel alloc] init];
        _updateLabel.textAlignment = NSTextAlignmentCenter;
        _updateLabel.font = [UIFont systemFontOfSize:13];
        _updateLabel.textColor = [UIColor grayColor];
    }
    return _updateLabel;
}

@end

一個簡單的下拉重新整理就這麼完成了,使用也很簡單:

[self.tableViewaddHeaderRefreshWithTarget:selfaction:@selector(doLoadData)];

-(void)doLoadData
{
    __weak ViewController1 *weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf.tableView endHeaderRefresh];
    });
}

以下這兩個方法都是直接對UI的操作 獨立開來

[self.tableViewbeginHeaderRefresh];

[self.tableViewendHeaderRefresh];


看一下效果吧:

(Gifrocket 製作gif)

大體的樣式完成了 接下來就可以根據自己的喜好自定義這個

CustomRefreshView,做一些酷炫的效果!

我的實現方式思路是參考這篇文章:http://blog.csdn.net/x6587305x/article/details/42640291