iOS开发笔记>> 下拉刷新,自定义UIControl

41 篇文章 0 订阅
6 篇文章 0 订阅

自定义下载刷新分析: 

1. 系统的下拉刷新 UIRefreshControl , 没有继承系统的下拉刷新, 而继承UIControl 

2. 自定义UIControl, 系统的下拉刷新默认有宽和高, 自定义的时候在init构造函数中设置固定的宽和高和y值

3. 添加子控件(菊花, 文字一些控件)设置约束

4. 下拉刷新线索: contentOffset. y值发生变化时, 对应的效果才会变化

5. 使用KVO监听 contentOffset 这个属性值的改变

6. 把监听方法移动到自定义的UIControl里

7. 分析下拉刷新的三种状态: 正常状态, 释放状态, 加载数据状态

8. 结合 contentOffset. y 和 UISrollView的dragging 这个属性来分析临界值

9. 记录这个状态值得改变, 分别实现对应的效果

10. 优化 - 用一个标记来排除状态的多次改变(这里用的是枚举)

11. 根据效果遇到一些小bug:

1. 加载中的时候视图下沉一下 

2. 加载数据需要调用UIControl 的 sendActionsForControlEvents 方法 

3. 实现一下加载完成后恢复视图的问题

 

下面直接上swift的代码(OC的话思路是一样的)

自定义CNRefreshView

//
//  CNRefreshView.swift
//  Sina
//
//  Created by 晟楠 on 16/5/4.
//  Copyright © 2016年 晟楠. All rights reserved.
//

import UIKit

// 记录状态的枚举
enum CNRefreshStatus: Int {
    
    /// 正常状态
    case Nomal = 1
    
    /// 释放去加载数据状态
    case pulling = 2
    
    /// 加载数据状态
    case loading = 3
    
}
class CNRefreshView: UIControl {

    /// UIScrollView类型的属性
    var scrollView: UIScrollView?
    
    /// 记录状态的属性
    var status: CNRefreshStatus = .Nomal {
        
        didSet {
            
            // 统一处理事件
            switch status {
                
            case .Nomal: messageLable.text = "下拉刷新"
                
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    
                    // 恢复箭头
                    self.arrowImageView.transform = CGAffineTransformIdentity
                })
                
                // 显示箭头
                arrowImageView.hidden = false
                
                // 停止菊花转动
                loadingView.stopAnimating()
                
                // 隐藏菊花
                loadingView.hidden = true
                
                
            case .pulling: messageLable.text = "释放刷新"
                
                // 旋转箭头
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    
                    self.arrowImageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
                })
                
            case .loading: messageLable.text = "加载中"
            
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    
                    // 让加载的view停留一下
                    var inset = self.scrollView!.contentInset
                    
                    inset.top = inset.top + 50
                    
                    self.scrollView?.contentInset = inset
                })
                
                // 隐藏箭头
                arrowImageView.hidden = true
                
                // 菊花开启
                loadingView.startAnimating()
                
                // 菊花显示
                loadingView.hidden = false
                
                // 是加载数据状态要去加载数据
                // 根据事件去触发方法
                sendActionsForControlEvents(UIControlEvents.ValueChanged)
            }
        }
    }
    
    // MARK: - 1.重写父类构造函数
    // 需要制定布局
    override init(frame: CGRect) {
        
      super.init(frame: frame)
        // 添加子控件和设置子控件约束
        setUpUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: 子控件添加到父控件上回调用此方法
    override func willMoveToSuperview(newSuperview: UIView?) {
        
        super.willMoveToSuperview(newSuperview)
        
        printLog(newSuperview)
        
        // 转换成UIScrollView, 集成UIScrollView的子类都可以用
        if newSuperview is UIScrollView {
            
            scrollView = newSuperview as? UIScrollView
            
            // MARK: - KVO监听tableView的contentOffset
            scrollView?.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
        }
        
    }
    
    // MARK: - 实现KVO要监听的方法
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        
        // 判断dragging进行更改标记
        if scrollView!.dragging {
            
            if scrollView?.contentOffset.y < -140 && status == .Nomal {
                
                status = .pulling
                
            } else if scrollView?.contentOffset.y >= -140 && status == .pulling {
                
                status = .Nomal
            }
            
        } else {
            
            if scrollView?.contentOffset.y < -140 && status == .pulling {
                
                status = .loading
                
            }
            
        }
    }
    
    // MARK: - 数据加载完成后调用
    func endRefreshing() {
        
        if status == .loading {
            
            // 让视图位置恢复
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                
                // 停留一下view
                var inset = self.scrollView!.contentInset
                
                inset.top = inset.top - 50
                
                self.scrollView?.contentInset = inset
                
            })
            
        }
        
        // 修改标记回到正常状态
        status = .Nomal
    }
    
    // MARK: 2.添加子控件和设置子控件约束
    private func setUpUI() {
        
        // 自定义子控件
        backgroundColor = UIColor.clearColor()
        
        // 设置宽高
        var frame = self.frame
        
        frame.size = CGSizeMake(kUIScreenWidth, 50)
        
        frame.origin.y = -50
        
        self.frame = frame
        
        // 添加控件
        addSubview(arrowImageView)
        
        addSubview(messageLable)
        
        addSubview(loadingView)
        
        // 设置约束
        loadingView.snp_makeConstraints { (make) -> Void in
            
            make.centerX.equalTo(self.snp_centerX).offset(-30)
            
            make.centerY.equalTo(self.snp_centerY)
            
        }
        
        arrowImageView.snp_makeConstraints { (make) -> Void in
            
            make.centerX.equalTo(self.snp_centerX).offset(-30)
            
            make.centerY.equalTo(self.snp_centerY)
        }
        
        messageLable.snp_makeConstraints { (make) -> Void in
            
            make.centerY.equalTo(self.snp_centerY)
            
            make.left.equalTo(arrowImageView.snp_right).offset(-5)
        }
        
     
    }
    // MARK: 3. 懒加载控件
    // 箭头
    private lazy var arrowImageView: UIImageView = {
       
        let img = UIImageView()
        
        img.image = UIImage(named: "tableview_pull_refresh")
        
        return img
        
    }()
    
    // 菊花
    private lazy var indicatorImageView: UIImageView = {
        
        let img = UIImageView()
        
        img.image = UIImage(named: "tableview_loading")
        
        return img
        
    }()
    
    // 提示语
    private lazy var messageLable: UILabel = {
        
       let lable = UILabel()
        
        lable.text = "下拉刷新"
        
        return lable
        
    }()
    
    private lazy var loadingView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
}

控制器CNHomeTableViewController, 只需要做懒加载, 监听, 添加即可

// 下拉刷新
    private lazy var refreshView: CNRefreshView = CNRefreshView()

// 添加下拉刷新视图
            tableView.addSubview(refreshView)

// 监听refreshView,当refreshView是加载数据时调用loadData
            refreshView.addTarget(self, action: "loadData", forControlEvents: UIControlEvents.ValueChanged)


在刷新数据之后调用 endRefreshing 方法停止即可
// 停止下拉刷新
            self.refreshView.endRefreshing()

代码如有哪里不妥可以加关注私信或评论给我, 谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值