一、需求
最多可以添加九张图片的自定义view,为满足9张的话,会有一张空白的可以点击继续添加的图片,同时图片可以点击查看大图,可以使用照相机拍照或者直接使用相册添加照片。
二、实现
由于实现的是九宫格方式的图片视图,采用了UICollectionView控件去实现,每一张图片使用UICollectionViewCell实现。添加图片以及图片的点击查看事件采用Block和代理实现。
三、主要代码
import UIKit
typealias PicBlock = (Int) -> Void //删除图片的回调protocol AddPictureCellDelegate: class {//一些可供调用的代理 func addImage(_ sender: PictureCell)//添加图片,传的参数是当前的cell func checkImage(at index: Int, _ sender: PictureCell)//查看某张图片,传的参数是位置、当前的cell func deleteImage(at index: Int, _ sender: PictureCell)//删除某张图片,传的参数是位置、当前的cell}
extension AddPictureCellDelegate{//可以调用,可以不调用
func addImage(_ sender: PictureCell){} func checkImage(at index: Int, _ sender: PictureCell){} func deleteImage(at index: Int, _ sender: PictureCell){}}/// 添加图片的单元格控件,最多添加9张,使用时必须设置TableView的estimatedRowHeightfilePrivate struct Metric {
static let pictureWidth:CGFloat = (Screen.Width - 4 * 16) / 3)
static let pictureHeight:CGFloat = pictureWidth:CGFloat
}
class PictureCell: UITableViewCell {
///显示的图片
var images = [Any]() {
didSet {
handleData(images)
updateCollectionViewHeight()
}
}
//PictureModel是根据实际情况创建的model
praivate var dataArray: [PictureModel] = []
///单元格标题
var title: String? {
didSet{
titleLabel.text = title
}
}
//
var attributedTitle: NSMutableAttributedString? {
didSet {
titleLabel.attributedText = attributedTitle
}
}
///是否可以滑动
var isScroll: Bool = true {
didSet {
collectionView.isScrollEnabled = isScroll
}
}
///最多显示9张
var maxCount = 9
///底部间距,最后一张图片到底步的距离
var bottomEdge: CGFloat = 4
///标题到顶部间距
var topEdge: CGFloat = 12 {
didSet {
titleLabel.snp.updateConstraints { (make) in
make.top.equalToSuperview().offset(topEdge)
}
}
}
//回调
var imageTappedCallBack: PicBlock?
var addImageCallBack: PicBlock?
var deleteCallBack: PicBlock?
//代理
weak var delegate: AddPictureCellDelegate?
// MARK: - lazy 标题
private lazy var titleLabel: UILabel = {
var label = UILabel()
label.textColor = UIColor.fwLightGray
label.font = YYFont.secondaryText.font
return label
}()
//collectionView
lazy var cellCollectionView: UICellCollectionView = {
let tempCollectionView = UICellCollectionView(frame: CGRect(x: 0, y: 0, width: Screen.Width, height: 100), collectionViewLayout: layout) tempCollectionView.longDragDelegate = self
tempCollectionView.longDragDataSource = self
tempCollectionView.backgroundColor = UIColor.white
tempCollectionView.fw_registerCellNib(ItemPictureCell.self)
return tempCollectionView
}()
private lazy var layout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 8
layout.minimumInteritemSpacing = 8
layout.sectionInset = UIEdgeInsets(top: 0, left: Edge.left, bottom: bottomEdge, right: 8)
layout.itemSize = CGSize.init(width: pictureWidth + 8, height: pictureHeight + 8)
layout.scrollDirection = .vertical
return layout
}()
// MARK: - init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
layOutUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//设置选中是是否有颜色的响应
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if (highlighted) {
cellCollectionView.backgroundColor = UIColor.fwE1E1E1
} else {
cellCollectionView.backgroundColor = UIColor.fwWhite
}
}
// MARK: - subviews
private func layOutUI() {
contentView.addSubview(titleLabel)
contentView.addSubview(cellCollectionView)
//布局标题
titleLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(Edge.left)
make.right.equalToSuperview().offset(Edge.right)
make.height.equalTo(YYFont.secondaryText.height)
make.top.equalToSuperview().offset(topEdge)
} //布局collectionView
cellCollectionView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom)
make.bottom.equalToSuperview()
make.height.equalTo(height).priority(999)
}
}
// MARK: - data
private func handleData(_ images: [Any]) {
dataArray.removeAll()
let picCount = images.count
let count = picCount == maxCount ? picCount : picCount + 1
for i in 0 ..< count {
if picCount != maxCount && i == count - 1 {
var model = DragedPictureModel()
dataArray.append(model)
} else {
let image = images[i]
var model = DragedPictureModel()
model.image = image
dataArray.append(model)
}
}
cellCollectionView.reloadData()
}
//更新collectionView的高度
private func updateCollectionViewHeight() {
cellCollectionView.snp.updateConstraints { [weak self] (make) in
guard let `self` = self else { return }
let height = self.calculateCollectionViewHeight()
make.height.equalTo(height).priority(999)
}
}
//根据图片数量计算当前的高度
private func calculateCollectionViewHeight() -> CGFloat {
let num = Float(dataArray.count) / 3
let rows = ceilf(num)
return CGFloat(rows) * (pictureHeight + 15) + 4
}
// MARK: - UICellCollectionViewDelegate, UICollectionViewDataSource
extension PictureCell : UICellCollectionViewDelegate,UICollectionViewDataSource {
//collectionView单元格数量
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.dataArr.count
}
//collectionView单元格创建
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath)
-> UICollectionViewCell {
if picCount != maxCount && indexPath.row == picCount - 1 {
let addCell = collectionView.fw_dequeueReusableCell(AddNewPictureCell.self, indexPath)
return addCell!
}else{
let imageCell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell",
for: indexPath) as! ImageItemCell
imageCell.model = dataArr[indexPath.row]
return imageCell
}
}
//event事件处理
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let picCount = images.count
if picCount != maxCount && indexPath.row == picCount - 1 {
if addImageCallBack != nil {
addImageCallBack?()
}
delegate?.addImage(self)
} else {
if imageTappedCallBack != nil {
imageTappedCallBack?(indexPath.row, self)
}
delegate?.checkImage(at: indexPath.row, self)
}
}
四、遇到的问题
项目中用到加载图片的库是Kingfisher,是大神Onevcat写的。
对于加载图片在项目中进行了封装,具体封装的加载图片的方法不在这里赘述,虽然这个库是当前Swift中使用加载图片最多的第三方,但是在如上加载图片的时候却出现了令人不可思议的一幕,就是有的时候完全没有问题,有的时候显示为空白的图片,把图片链接放在浏览器中也能打开,但是显示不出来。
对以上的问题进行了排查,也搜索了相关的方法,尝试了很多的解决办法,很多不能够解决,有效的就两种;
本人采用的就是第一种:使用了OC中广为使用的SDWebImage替换了Kingfisher,亲测可用,也从侧面反映了封装的必要性。
第二种方法:Kingfisher的作者在回复中提到的,因为加载图片过快,可以控制延缓加载图片,具体的操作不得而知。
五、总结
最终解决了问题还是令人高兴的事,断点查看每一步到别人写的库的内部代码之后,看到别人的代码考虑之深、考虑得相当全面,由衷的佩服。
目前已经使用Swift开发了两年,虽然在语法上优化了很多,让开发者省去了不少的代码,但是在开发与硬件交互的过程中,感觉OC更好一点,因为大多数硬件交互的还要用编译一些C++的文件,Swift这事就不能直接掉用了。
Swift是苹果目前推荐使用的开发语言,应该会不断完善,保持初心,继续努力学习!