架构思想篇-如何写好设置界面这类静态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<Bool>) 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
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,275评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 在没有生小孩之前,一直觉得带孩子好累呀!哪里都不能去,而且不能上班,要做家庭主妇,万一老公不负责任的,手心向背,朝...
    钱夫人爱自由阅读 310评论 0 2
  • 蒋飞燕,如果你不认识她,你见她第一眼,你会喜欢上她的。 因为她长得浓眉大眼,小方圆脸,皮肤白净,再加...
    心的归途阅读 311评论 0 0
  • 年年月月里 相互取暖 假装寒暄 有谁记得 这世界你曾经来过 人来人往 是浮华时代的集体失忆
    留子尧阅读 194评论 0 2