架构思想篇-如何写好设置界面这类静态tableView

之前一直是用OC语言写的,也学过一些简单的Swift语法,但是真正开始使用Swift,也就是一个月之前。所以从此之后开始改用Swift语言写博客文章。该篇文章主要说一下一般的静态页面的实现方案、优缺点分析以及我自己的实现方法。该篇文章技术含量只能算是很低,比起那些封装自己的tablView框架的博主相比,是小巫见大巫。主要是想把自己的一种思想写出来,以后碰到类似的页面能毫不犹豫的快速写出,并且写出的代码在不同的项目中都可以直接拿来使用。

一、常见的几种实现方案以及优缺点分析

使用UIScrollView来实现

实际开发中或许会有些人选择这种非常不佳的方案来实现这种静态tableView。
根据经验来看,这类写法基本不可取,主要原因有以下几点:
1、内容比较多的时候,子View数量会很多,这样一方面由于添加子View的代码过多引起 Controller 变得庞大,另一方面子View很多时,对子View的布局容易出现错误,不易排查。
2、针对第一点,可能会有开发人员将子View按行进行分块,并封装成一个较大的 View,这样会减少 UIScrollView 直接管理的子View数量,一定程度上避免了第一点问题。
3、但是,当设计师需要移动不同行的顺序,或者在某一位置添加了一行新的内容,这时候修改布局大概会令人吐血了吧。
4、如果某一行视图需要点击,则需要为该行视图添加手势或者采用 UIButton,并设置好点击的回调;当页面视图行数较多时,会导致代码杂乱。

使用xib实现

使用xib可能是个不错的选择,但是一般都是使用Autolayout进行布局,一旦布局上面出现了问题,可能不如直接使用第三方约束修更直接。

使用tableView实现

首先说明,我的最终实现肯定也是通过tableView实现的,只是在使用tableView时讲很多事件以及布局集中到一块。
相比使用xib以及UIScrollView,tableView无疑是好了很多,但是很多人在使用tableView布局这类静态cell的时候同样会遇到一些问题。响应事件不集中(cell的点击事件或cell上的子控件的点击事件),布局不同的cell要注册不同的cell,二本文恰恰就是对着几个问题进行处理。

二、我的实现方法

我的实现方法最终是这样的形式,和常规的使用tableView去实现这类静态cell相比,主要是把各种事件都统一集中起来管理(如cell的布局,cell的点击事件,UISwitch的点击事件,退出按钮的点击事件)。这样当静态页面布局有所变化的时候,只要集中更改一两块地方即可,不用再在整个项目中这里改一下哪里改一下。都在返回UITableView

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell:StaticTableViewCell? = tableView.dequeueReusableCell(withIdentifier: guildCellId) as! StaticTableViewCell?
        if cell == nil {
            cell = StaticTableViewCell(style: .default, reuseIdentifier: guildCellId)
        }
        guard let subViews = cell?.contentView.subviews else { return  cell!}
        for sub in subViews {
            sub.removeFromSuperview()
        }
       
            cell?.height = kNormalCellH
            if indexPath.row == 0{//群聊名称
                cell?.selectionStyle = .default
                cell?.setCellStyleLeftTextAndRightImage(leftText: "群聊名称", image: #imageLiteral(resourceName: "rightArrow"), imageName: nil, urlStr: nil)
                cell?.cellClickClosure = { [weak self] cell in
                    printLog("群名称")
                }
            }else if indexPath.row == 1{//群二维码
                cell?.selectionStyle = .default
                cell?.setCellStyleLeftTextAndRightImage(leftText: "群二维码", image: self.qrCodeImage, imageName: nil, urlStr: nil)
                cell?.cellClickClosure = { [weak self] cell in
                    self?.showQRCodeImage()
                }
            }else if indexPath.row == 2{//屏蔽消息
                cell?.selectionStyle = .none
                guard let isON = GOLocalData.shared.isReceiveGuildMessage else { printLog("isReceiveGuildMessage为空"); return cell!}
                cell?.setCellSwitchStyle(leftText: "屏蔽消息", sIsOn: isON, switchClosure: {[weak self] (isOn) in
                    if isOn{
                        print("开")
                        GOLocalData.shared.isReceiveGuildMessage = true
                    }else{
                        print("关")
                        GOLocalData.shared.isReceiveGuildMessage = false
                    }
                })
            }else if indexPath.row == 3{//退出并删除
                cell?.selectionStyle = .none
                cell?.height = kQuitCellH
                cell?.setCellLoginOutStyle(btnTitle: "退出并删除", logoutClosure: { [weak self] in
                    printLog("退出按钮点击")
                })
                cell?.cellClickClosure = { [weak self] cell in
                    //printLog("退出")
                }
            }
        return  cell!
    }

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 3{
            return kQuitCellH
        }
        return kNormalCellH
    }

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let cell = tableView.cellForRow(at: indexPath) as! StaticTableViewCell
        cell.cellClickClosure?(cell)
    }

在UITableViewCell类中,我将每一个单一的控价都单独给封装起来,如箭头、头像、单独的文字label、分割线等,外部使用的时候只需要将这些当以的控件组装起来(在需要的时候更改一下frame即可),如创建一个左边是文字右边是UISwitch并带有分割线的cell,只需要在cell中单独添加一个方法调用四行代码即可。请看下面代码 func setCellSwitchStyle(leftText:String?,sIsOn:Bool,switchClosure:((_ isON: Bool)->())?)方法。

如此一来,随着项目的不断完善针对每种单一的控件我们都可以单独封装一下,之后在开发其他app的时候直接把整个UITableViewCell类拉到项目中,并可快速直接使用。其实严格意义上来说我应该通过属性记录封装每一个控件,而不是直接这样写。只是简单的写一下,主要是想说一下这种思想,相比于通篇都是if else if else if else 这样来布局的话,这样的管理稍微省了一些事。

class StaticTableViewCell: UITableViewCell {
    
    fileprivate lazy var bag = DisposeBag()
    var cellHeight: CGFloat = 0//cell的高度
    var cellClickClosure: ((StaticTableViewCell)->())?//cell的点击回调
    var switchClosure:((_ isOn:Bool)->())?//switch回调
    var logoutClosure:(()->())?//退出按钮回调
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.selectionStyle = .gray
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
        
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
    }

}


// MARK: - 对外公开的方法
extension StaticTableViewCell{
    //styleOne 左边文字,右边图片(传入图片名或image)
    func setCellStyleLeftTextAndRightImage(leftText:String?,image:UIImage?,imageName:String?,urlStr:String? = nil){
        self.contentView.addSubview(self.createLabel(leftText: leftText))
        if image != nil {
            self.contentView.addSubview(self.createImageView(imageName: imageName, urlStr: nil, img: image))
        }
        if imageName != nil {
            self.contentView.addSubview(self.createImageView(imageName: imageName, urlStr: nil, img: nil))
        }
        if urlStr != nil {
            self.contentView.addSubview(self.createImageView(imageName: imageName, urlStr: urlStr, img: image))
        }
        //line
        self.contentView.addSubview(gapLine())
    }
    
    //退出按钮style
    func setCellLoginOutStyle(btnTitle:String?,logoutClosure:(()->())?){
        self.logoutClosure = logoutClosure
        self.contentView.addSubview(self.createQuitBtn(btnTitle: btnTitle))
    }
    
    //switch风格(左文字 右switch)
    func setCellSwitchStyle(leftText:String?,sIsOn:Bool,switchClosure:((_ isON: Bool)->())?){
        self.switchClosure = switchClosure
        self.contentView.addSubview(self.createLabel(leftText: leftText))
        //switch
        self.contentView.addSubview(createSwitch(sIsOn: sIsOn))
        //line
        self.contentView.addSubview(gapLine())
    }
}




// MARK: - 创建单一控件
extension StaticTableViewCell{
    //创建右边的UISwitch
    fileprivate func createSwitch(sIsOn:Bool) -> UISwitch {
        let s = UISwitch()
        //s.addTarget(self, action: #selector(switchAction(s:)), for: .valueChanged)
        //FIXME:---这里实际上应该是用模型记录,暂时为了防止复用出现问题,临时用属性记录
        //s.isOn = false
        s.isOn = sIsOn
        s.bounds = CGRect(x: 0, y: 0, width:41, height: 24)
        s.center = CGPoint(x: (kScreenW - CGFloat(41/2.0) - kMargin), y: self.bounds.height/2)
        //s.tintColor = ColorUtil.mainBlueColor()//边框颜色
        s.onTintColor = ColorUtil.mainBlueColor()
        //s.thumbTintColor = ColorUtil.mainWhiteColor()//滑块颜色
        self.contentView.addSubview(s)
        s.rx.value.subscribe { (event:Event) in
            guard let element = event.element else {
                return
            }
            self.switchClosure?(element)
            }.addDisposableTo(bag)
        return s
    }
    
    //创建退出按钮
    fileprivate func createQuitBtn(btnTitle:String?)->UIButton{
        let quitBtn = UIButton()
        quitBtn.bounds = CGRect(x: 0, y: 0, width: 153, height: 44)
        quitBtn.center = CGPoint(x: kScreenW/2.0, y: self.bounds.height/2.0)
        quitBtn.setBackgroundImage(#imageLiteral(resourceName: "deleteQuit"), for: .normal)
        quitBtn.setTitle(btnTitle, for: .normal)
        quitBtn.titleLabel?.textColor = ColorUtil.mainWhiteColor()
        quitBtn.titleLabel?.font = UIFont.systemFont(ofSize: 18)
        self.contentView.addSubview(quitBtn)
        quitBtn.rx.tap.subscribe { (event: Event<()>) in
            self.logoutClosure?()
            }.addDisposableTo(bag)
        return quitBtn
    }
    
    //创建label
    fileprivate func createLabel(leftText:String?)->UILabel {
        let label = UILabel()
        label.text = leftText
        label.textColor = kTextColor
        label.sizeToFit()
        label.frame = CGRect(x:kMargin , y:(self.bounds.height - label.height)/2.0, width: label.width, height: label.height)
        //self.contentView.addSubview(label)
        return label
    }
    //创建imageView
    fileprivate func createImageView(imageName:String? = nil, urlStr:String? = nil,img:UIImage? = nil)->UIImageView{
        
        let IV = UIImageView()
        var image:UIImage = UIImage()
        if imageName != nil{
            image = UIImage(named: imageName!)!
            IV.image = image
        }
        if img != nil {
            image = img!
            IV.image = image
        }
        if urlStr != nil {
            IV.kf.setImage(with: URL(string: urlStr!))
        }
        IV.bounds = CGRect(x: 0, y: 0, width:20, height: 20)
        IV.center = CGPoint(x: kScreenW - 20/2.0 - kMargin, y: self.bounds.height/2)
        return IV
    }
    
    //创建分割线
    fileprivate func gapLine() -> UIView {
        let view = UIView(frame: CGRect(x: kMargin, y: self.bounds.height - 1/UIScreen.main.scale, width: kScreenW - 2 * kMargin, height: 1/UIScreen.main.scale))
        view.backgroundColor = kTextColor.withAlphaComponent(0.5)
        return view
    }
}

你可能感兴趣的:(架构思想篇-如何写好设置界面这类静态tableView)