如何创建从数组加载数据展示一排UIImageView的类 解决CollectionViewCell布局异常问题
问题根因
你当前代码的核心问题是所有视图添加、布局计算逻辑都耦合在SDWebImage的异步图片下载回调中:
- 图片下载返回顺序不受遍历顺序控制,导致
spacing变量累加顺序错乱,头像位置不符合预期 - 子视图添加顺序随机,导致头像层级覆盖和预期相反
- 页面返回时图片已经有本地缓存,回调顺序和遍历顺序一致,所以展示恢复正常,和你观察到的现象完全吻合
优化实现方案
核心改造点
- 提前创建所有头像视图,先完成布局占位,再触发图片下载,图片下载完成后直接赋值即可,不影响布局逻辑
- 用遍历索引直接计算每个头像的左间距,不要在异步回调中累加间距变量
- 新增数据更新逻辑,适配UICollectionViewCell复用场景,避免复用时出现旧视图残留
优化后代码
import Foundation import UIKit class UserStack: UIView{ var size: CGFloat = 50 // 加didSet监听,支持数据更新/Cell复用 var userArray: [UserModel]! { didSet { // 先清空旧子视图 subviews.forEach { $0.removeFromSuperview() } renderView() } } convenience init(userArray: [UserModel], size: CGFloat = 50) { self.init(frame: .zero) self.size = size self.userArray = userArray } override init(frame: CGRect) { super.init(frame: frame) } private func renderView() { let maxDisplayCount = 4 let displayCount = min(userArray.count, maxDisplayCount) let itemOffset = size - 10 for i in 0..<displayCount { let isLast = i == displayCount - 1 && userArray.count > maxDisplayCount let currentUser = isLast ? userArray.last! : userArray[i] if !isLast { // 普通头像 let imageView = createAvatarImageView() addSubview(imageView) // 提前算好位置,不受异步逻辑影响 let leftPadding = CGFloat(i) * itemOffset imageView.setDimensions(height: size, width: size) imageView.centerY(inView: self) imageView.anchor(left: leftAnchor, paddingLeft: leftPadding) // 单独处理图片加载 guard let url = currentUser.imageURL else { return } imageView.sd_setImage(with: URL(string: url)) } else { // 带+N标识的末尾头像 let numberImageView = createAvatarImageView() addSubview(numberImageView) let leftPadding = CGFloat(i) * itemOffset numberImageView.setDimensions(height: size, width: size) numberImageView.centerY(inView: self) numberImageView.anchor(left: leftAnchor, paddingLeft: leftPadding) // 系统模糊层效果更自然 let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) blurView.layer.cornerRadius = size * 0.4 blurView.layer.masksToBounds = true blurView.layer.borderWidth = 0.75 blurView.layer.borderColor = UIColor.white.cgColor numberImageView.addSubview(blurView) blurView.setDimensions(height: size, width: size) blurView.center(inView: numberImageView) let numberLabel = UILabel() numberLabel.text = "+\(userArray.count - 3)" numberLabel.font = .poppinsMedium(size: 14) numberLabel.textColor = .white blurView.contentView.addSubview(numberLabel) numberLabel.center(inView: blurView) // 单独处理图片加载 guard let url = currentUser.imageURL else { return } numberImageView.sd_setImage(with: URL(string: url)) } } } // 抽离公共的头像创建逻辑,减少重复代码 private func createAvatarImageView() -> UIImageView { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleAspectFill imageView.layer.cornerRadius = size * 0.4 imageView.layer.masksToBounds = true imageView.layer.borderWidth = 0.75 imageView.layer.borderColor = UIColor.white.cgColor return imageView } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
适配CollectionView复用的使用方式
不需要每次都重新创建UserStack实例,在Cell复用周期内更新数据即可:
// Cell复用前清空旧数据 override func prepareForReuse() { super.prepareForReuse() userStack.userArray = [] } // 配置Cell数据 func config(with users: [UserModel]) { userStack.userArray = users }
内容的提问来源于stack exchange,提问作者Andrew Kestler
相关产品推荐
相关产品推荐

