Swift:玩安卓App——InfoViewCell页面与布局编写

这是我参与更文挑战的第26天,活动详情查看: 更文挑战

iOS一个典型面经

在iOS开发中,经常会被问的一个面试题就是如何优化TableView?

这个问题可以算是面试造火箭,因为你的Cell承载的内容级别不同,需要优化着手的地方也就不同,面试的时候,只能泛泛而聊,而不能具体到细节。

有关如何优化TableView,网上文章一堆了,我就不在大佬面前班门弄斧了。

但是这次玩安卓App上的Cell,还是有些内容能够聊一下的。

分析页面

IMG_0442.PNG

上面的这个图就是首页的Cell的一些形式:

  • Cell可能有图片,可能没有图片。Info模型中envelopePic可能有值,可能没有值。

  • Cell的高度不确定。Info模型中title的字符串长度不确定

  • 作者名称可能有或者没有。Info模型中author字段可能有值,可能没有值。

  • 赞称可能有或者没有。Info模型中zan字段可能有值,可能没有值,并且在zan的数量大于0时才显示。

image.png

从上面的分析可以看出,带有image,右侧有title,而title下方有author,zan和author同高,并且author与zan距离底部的间距一致,这是该Cell的基本布局。

在赋值模型的时候,根据envelopePic字段是否有值来确定image是否隐藏,右侧的title、author、zan是否随之改变布局。

难点就是如何让高度可以变化,这一点,我们在代码中体现。

好了上面分析完了,上代码,里面有详细注释,请注意查看:

import UIKit

class InfoViewCell: UITableViewCell {
    
    /// info模型的set与get
    var info: Info! {
        /// set方法
        set {
            _info = newValue
            
            /// 拿出titile
            var title = newValue.title
            /// 去掉title中的html标签,赋值给contentLabel
            contentLabel.text = title?.filterHTML()
            
            /// 赋值authorLabel
            authorLabel.text = newValue.author
            
            /// 判断zan有值,而且大于0,赋值给praiseLabel
            if let zan = newValue.zan, zan > 0 {
                praiseLabel.text = "赞: \(zan)"
            }
            
            /// 重点
            /// 判断envelopePic是否有值
            if let imageString = info.envelopePic, let url = URL(string: imageString) {
            
                /// envelopePic有值,不隐藏picView
                picView.isHidden = false
                /// 使用Kingfisher进行图片加载
                picView.kf.setImage(with: url, placeholder: R.image.saber())
                
                /// 重置布局为初始布局,由于Cell会被复用,如果重置,直接用,可能从复用池捞出来的是改变了布局的Cell,显示会有异常
                contentLabel.snp.remakeConstraints { make in
                    make.leading.equalTo(picView.snp.trailing).offset(16)
                    make.trailing.equalTo(contentView).offset(-16)
                    make.top.equalTo(contentView).offset(10)
                }
                
                authorLabel.snp.remakeConstraints { make in
                    make.leading.equalTo(contentLabel)
                    make.top.equalTo(contentLabel.snp.bottom).offset(10)
                    make.bottom.equalTo(contentView.snp.bottom).offset(-10)
                }
                
            }else {
            
                /// envelopePic没有值,隐藏picView
                picView.isHidden = true
                
                /// contentLabel和authorLabel布局向左偏移,重新布局
                contentLabel.snp.remakeConstraints { make in
                    make.leading.equalTo(contentView).offset(16)
                    make.trailing.equalTo(contentView).offset(-16)
                    make.top.equalTo(contentView).offset(10)
                }
                
                authorLabel.snp.remakeConstraints { make in
                    make.leading.equalTo(contentLabel)
                    make.top.equalTo(contentLabel.snp.bottom).offset(10)
                    make.bottom.equalTo(contentView.snp.bottom).offset(-10)
                }
            }
        } get {
            /// get方法
            return _info
        }
    }
    
    /// 有点像Dart中的对私有变量以_开头,哈哈,其实在OC时代,Model的set方法不是这样的
    private var _info: Info!
    
    /// 懒加载图片
    private lazy var picView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()
    
    /// 懒加载内容Label
    private lazy var contentLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.font = UIFont.systemFont(ofSize: 15)
        label.textColor = .black
        return label
    }()
    
    /// 懒加载作者label
    private lazy var authorLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 11)
        label.textColor = .gray
        return label
    }()
    
    /// 懒加载点赞label
    private lazy var praiseLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 11)
        label.textColor = .gray
        return label
    }()
    
    /// 初始化方法中进行布局
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 布局方法细化
    private func setupUI() {
        /// 右侧有箭头
        accessoryType = .disclosureIndicator
        
        /// 添加图片
        contentView.addSubview(picView)
        picView.snp.makeConstraints { make in
            /// 距Cell左侧16
            make.leading.equalTo(contentView).offset(16)
            /// 居Cell的Y轴正中
            make.centerY.equalTo(contentView)
            /// 图片宽高44
            make.width.height.equalTo(44)
        }
        
        /// 添加内容
        contentView.addSubview(contentLabel)
        contentLabel.snp.makeConstraints { make in
            /// label的左边距图片的右侧16
            make.leading.equalTo(picView.snp.trailing).offset(16)
            /// label的右边距Cell的右侧16
            make.trailing.equalTo(contentView).offset(-16)
            /// label的顶部距Cell的顶部10
            make.top.equalTo(contentView).offset(10)
        }
        
        /// 添加点赞
        contentView.addSubview(praiseLabel)
        praiseLabel.snp.makeConstraints { make in
            /// 右侧等于内容的右侧,即label的右边距Cell的右侧16
            make.trailing.equalTo(contentLabel)
            /// 顶部距离contentLabel的底部10
            make.top.equalTo(contentLabel.snp.bottom).offset(10)
            /// 底部距离Cell的底部向上偏移10,这一行非常关键,是用来撑满Cell使得高度动态化的关键
            make.bottom.equalTo(contentView.snp.bottom).offset(-10)
        }
        
        /// 添加作者
        contentView.addSubview(authorLabel)
        authorLabel.snp.makeConstraints { make in
            /// 左侧等于内容的左侧,即距Cell左侧16
            make.leading.equalTo(contentLabel)
            /// 顶部与底部参照点赞
            make.top.bottom.equalTo(praiseLabel)
        }
    }
}

复制代码

因为上面分析中已经非常详细的讲解了布局思路,所以这里只说如何撑满整个Cell,不知道大家对于我之前的文章——Swift:布局库——SnapKit|周末学习最后一段SnapKit与UIScrollView还有没有印象?

没印象的话快去读一读,撑满Cell的秘诀就是布局在Cell中的最后一个subView,使用它的bottom与Cell的contentView去做约束,它决定了Cell的动态高度!也就这段代码中最后一段:make.bottom.equalTo(contentView.snp.bottom).offset(-10):

/// 添加点赞
contentView.addSubview(praiseLabel)
praiseLabel.snp.makeConstraints { make in
    /// 右侧等于内容的右侧,即label的右边距Cell的右侧16
    make.trailing.equalTo(contentLabel)
    /// 顶部距离contentLabel的底部10
    make.top.equalTo(contentLabel.snp.bottom).offset(10)
    /// 底部距离Cell的底部向上偏移10,这一行非常关键,是用来撑满Cell使得高度动态化的关键
    make.bottom.equalTo(contentView.snp.bottom).offset(-10)
}
复制代码

需要注意的是,在Cell中添加subView,最后是添加到Cell的contentView上,直接加到Cell当然没有问题,但是在本例子有系统的右侧箭头,或者侧滑的时,是会有异常的!

引用一段


1、cell.addSubview

2、cell.contentView.addSubview

区别在于进行cell编辑时,比如cell内容向左移或者右移时,第一种方式子视图不会移动,第二可以,所以这种情况一般使用第二种方式。

还有在设置backgroundColor时,使用cell时左移或者右移颜色是不会变的,而用cell.contentView时,移动后的空白会显示cell的默认颜色,这种情况视实际情况选择。

总结:cell.contentView添加子控件的时候,相当于直接往cell上方添加子控件,独立于cell的存在的。而cell添加子控件相当于往cell上添加,跟cell是一体的。


另外,请不要尝试用UITableViewCell本身自带的textLabel、detailTextLabel、imageView通过SnapKit改变布局来使得Cell能够撑满Cell,我试了,不能成功。

最后我也有一个疑问,有大佬能指导一下我就好了:

info的set方法中,我使用的remakeConstraints去更新约束,我之前使用的updateConstraints,但是很遗憾崩溃了,如果有知道为啥,可否告诉我一下?

总结

本篇主要讲解了首页列表中的InfoViewCell的布局和其中的小技巧与注意事项。

通过前三天的文章(编写HomeViewModel、编写基类控制器、编写首页)以及本篇,我们基本上完成了玩安卓App的首页编写,这个页面的编写完成,基本上可以说整个玩安卓App的页面开发思路基本完成。

大家沿着这个思路走,可以逐步完成其他页面的编写。

明日继续

首页编写完了,我准备把登录、用户相关的账号模块拿出来讲一下。

因为玩安卓App,有几个接口设计必须登录成功后才能拿到数据。

大家加油!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享