如何从数组中创建一个在一行中显示多个 UIImageViews 的类?
Posted
技术标签:
【中文标题】如何从数组中创建一个在一行中显示多个 UIImageViews 的类?【英文标题】:How to create a class that shows multiple UIImageViews in a line from an Array? 【发布时间】:2021-09-24 00:48:33 【问题描述】:我在尝试在集合视图单元格中创建可视元素时遇到了一个奇怪的错误。该类采用一组 UserModels 并将所有个人资料图片对齐一行。如果数组中有超过 4 个用户,则最后一个图像视图是带有“+N”的模糊图像,以让用户知道有更多用户无法显示在屏幕上。
实际行为:
预期行为:
一旦您转到另一个屏幕并返回,图像就会自行校正。
这是我为获得以下结果而创建的类。
import Foundation
import UIKit
class UserStack: UIView
var userArray: [UserModel]!
var size: CGFloat = 50
init(userArray: [UserModel])
super.init(frame: .zero)
self.userArray = userArray
if self.userArray.count <= 4
fourOrLess()
else
fiveOrMore()
func fourOrLess()
var spacing: CGFloat = 0
userArray.forEach user in
let imageView = UIImageView()
guard let url = user.imageURL else return
imageView.sd_setImage(with: URL(string: url)) i, e, c, u in
if let e = e
print("DEBUG: Error setting userStack - \(e)")
return
self.addSubview(imageView)
self.bringSubviewToFront(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = self.size * 0.4
imageView.layer.masksToBounds = true
imageView.layer.borderWidth = 0.75
imageView.layer.borderColor = UIColor.white.cgColor
///LAYOUT
imageView.setDimensions(height: self.size, width: self.size)
imageView.centerY(inView: self)
imageView.anchor(left: self.leftAnchor, paddingLeft: spacing)
//DispatchQueue.main.asyncAfter(deadline: .now() + 0.01)
spacing += (self.size - 10)
//
func fiveOrMore()
var count: Int = 1
var spacing: CGFloat = 0
userArray.forEach user in
if count < 4
count += 1
let imageView = UIImageView()
guard let url = user.imageURL else return
imageView.sd_setImage(with: URL(string: url)) i, e, c, u in
if let e = e
print("DEBUG: Error setting userStack - \(e)")
return
self.addSubview(imageView)
self.bringSubviewToFront(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = self.size * 0.4
imageView.layer.masksToBounds = true
imageView.layer.borderWidth = 0.75
imageView.layer.borderColor = UIColor.white.cgColor
///LAYOUT
imageView.setDimensions(height: self.size, width: self.size)
imageView.centerY(inView: self)
imageView.anchor(left: self.leftAnchor, paddingLeft: spacing)
spacing += (self.size - 10)
else
///NUMBER VIEW
let numberImageView = UIImageView()
let blurView = UIView()
let numberLabel = UILabel()
guard let _URL = userArray.last?.imageURL else return
numberImageView.sd_setImage(with: URL(string: _URL)) i, e, c, u in
if let e = e
print("DEBUG: Error setting userStack - \(e)")
return
self.addSubview(numberImageView)
numberImageView.translatesAutoresizingMaskIntoConstraints = false
numberImageView.contentMode = .scaleAspectFill
numberImageView.layer.cornerRadius = self.size * 0.4
numberImageView.layer.masksToBounds = true
//numberImageView.layer.borderWidth = 0.75
//numberImageView.layer.borderColor = UIColor.white.cgColor
numberImageView.addSubview(blurView)
blurView.translatesAutoresizingMaskIntoConstraints = false
blurView.backgroundColor = .init(white: 0.2, alpha: 0.9)
blurView.layer.cornerRadius = self.size * 0.4
blurView.layer.masksToBounds = true
blurView.layer.borderWidth = 0.75
blurView.layer.borderColor = UIColor.white.cgColor
blurView.addSubview(numberLabel)
numberLabel.text = "+\(self.userArray.count - 3)"
numberLabel.font = .poppinsMedium(size: 14)
numberLabel.textColor = .white
///LAYOUT
numberImageView.setDimensions(height: self.size, width: self.size)
numberImageView.centerY(inView: self)
numberImageView.anchor(left: self.leftAnchor, paddingLeft: spacing)
numberImageView.bringSubviewToFront(blurView)
blurView.setDimensions(height: self.size, width: self.size)
blurView.center(inView: numberImageView)
numberLabel.center(inView: blurView)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
UIHelper 函数:
extension UIView
func anchor(top: NSLayoutYAxisAnchor? = nil,
left: NSLayoutXAxisAnchor? = nil,
bottom: NSLayoutYAxisAnchor? = nil,
right: NSLayoutXAxisAnchor? = nil,
paddingTop: CGFloat = 0,
paddingLeft: CGFloat = 0,
paddingBottom: CGFloat = 0,
paddingRight: CGFloat = 0,
width: CGFloat? = nil,
height: CGFloat? = nil)
translatesAutoresizingMaskIntoConstraints = false
if let top = top
topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
if let left = left
leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
if let bottom = bottom
bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
if let right = right
rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
if let width = width
widthAnchor.constraint(equalToConstant: width).isActive = true
if let height = height
heightAnchor.constraint(equalToConstant: height).isActive = true
func center(inView view: UIView, yConstant: CGFloat? = 0)
translatesAutoresizingMaskIntoConstraints = false
centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: yConstant!).isActive = true
func centerX(inView view: UIView, topAnchor: NSLayoutYAxisAnchor? = nil, paddingTop: CGFloat? = 0)
translatesAutoresizingMaskIntoConstraints = false
centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
if let topAnchor = topAnchor
self.topAnchor.constraint(equalTo: topAnchor, constant: paddingTop!).isActive = true
func centerY(inView view: UIView, leftAnchor: NSLayoutXAxisAnchor? = nil,
paddingLeft: CGFloat = 0, constant: CGFloat = 0)
translatesAutoresizingMaskIntoConstraints = false
centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: constant).isActive = true
if let left = leftAnchor
anchor(left: left, paddingLeft: paddingLeft)
func setDimensions(height: CGFloat, width: CGFloat)
translatesAutoresizingMaskIntoConstraints = false
heightAnchor.constraint(equalToConstant: height).isActive = true
widthAnchor.constraint(equalToConstant: width).isActive = true
如何让这个类更可靠,以便每次都能得到预期的结果?
我在想也许制作一个 UIImageView 数组并将其返回到我的集合视图单元格。
【问题讨论】:
什么是UIImage方法sd_setImage()
?它是从哪里来的,它有什么作用?向我们展示该代码,或指向它所来自框架的文档的链接。
这一行没有意义,应该会产生一个编译器错误:imageView.setDimensions(height: self.size, width: self.size)
(但是你没有显示setDimensions的代码。你需要提供所有相关函数的代码您正在调用您发布的代码。
sd_setImage() 来自 SDWebImage cocoapod
我已在帖子中添加了 .setDimensions 扩展名
那么 sd_setImage 函数有什么作用呢?为什么需要关闭?闭包的所有这些参数是什么意思? (不要强迫你寻求帮助的人问你一堆问题,然后必须去寻找你正在使用的框架,阅读它们,然后做一堆工作。编辑你的问题到包括读者理解您的问题所需的所有信息。)
【参考方案1】:
您不应该在没有某种形式的同步的情况下在循环中调用异步方法,否则会产生奇怪的副作用。我敢打赌,这就是您的结果不一致的原因。
先下载所有图片,然后将它们显示在您的视图中。
您可以使用 combine 非常轻松地获取所有图像。
struct ImageLoader
let urls: [URL]
func publish() -> AnyPublisher<[UIImage], Error>
urls.publisher
.flatMap
URLSession.shared.dataTaskPublisher(for: $0)
.mapError $0 as Error
.compactMap $0.data
.compactMap UIImage(data: $0)
.collect()
.eraseToAnyPublisher()
你可以像这样使用 ImageLoader:
var subscriptions = Set<AnyCancellable>()
func fetchImages(_ urls: [URL], completion: @escaping ([UIImage]) -> Void)
ImageLoader(urls: urls)
.publish()
.receive(on: DispatchQueue.main)
.sink result in
print("Result: \(result)")
receiveValue: images in
completion(images)
.store(in: &subscriptions)
对于您的 UserStack,您可以执行以下操作:
class UserStack2: UIView
var size: CGFloat = 50
init(images: [UIImage])
super.init(frame: .zero)
addImages(images)
func createImageView(with image: UIImage, spacing: CGFloat)
let imageView = UIImageView(image: image)
self.addSubview(imageView)
self.bringSubviewToFront(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = self.size * 0.4
imageView.layer.masksToBounds = true
imageView.layer.borderWidth = 0.75
imageView.layer.borderColor = UIColor.white.cgColor
imageView.setDimensions(height: self.size, width: self.size)
imageView.centerY(inView: self)
imageView.anchor(left: self.leftAnchor, paddingLeft: spacing)
func createPlaceHolderView(withValue value: Int, spacing: CGFloat)
let numberImageView = UIImageView()
let blurView = UIView()
let numberLabel = UILabel()
self.addSubview(numberImageView)
numberImageView.translatesAutoresizingMaskIntoConstraints = false
numberImageView.contentMode = .scaleAspectFill
numberImageView.layer.cornerRadius = self.size * 0.4
numberImageView.layer.masksToBounds = true
//numberImageView.layer.borderWidth = 0.75
//numberImageView.layer.borderColor = UIColor.white.cgColor
numberImageView.addSubview(blurView)
blurView.translatesAutoresizingMaskIntoConstraints = false
blurView.backgroundColor = .init(white: 0.2, alpha: 0.9)
blurView.layer.cornerRadius = self.size * 0.4
blurView.layer.masksToBounds = true
blurView.layer.borderWidth = 0.75
blurView.layer.borderColor = UIColor.white.cgColor
blurView.addSubview(numberLabel)
numberLabel.text = "+\(value)"
// numberLabel.font = .poppinsMedium(size: 14)
numberLabel.textColor = .white
numberImageView.setDimensions(height: self.size, width: self.size)
numberImageView.centerY(inView: self)
numberImageView.anchor(left: self.leftAnchor, paddingLeft: spacing)
numberImageView.bringSubviewToFront(blurView)
blurView.setDimensions(height: self.size, width: self.size)
blurView.center(inView: numberImageView)
numberLabel.center(inView: blurView)
func addImages(_ images: [UIImage])
let maxImages = 4
var spacing: CGFloat = 0
images.prefix(maxImages).forEach image in
createImageView(with: image, spacing: spacing)
spacing += (self.size - 10)
if images.count > maxImages
createPlaceHolderView(withValue: images.count - maxImages, spacing: spacing)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
【讨论】:
确定已更新。我还添加了一些说明,说明为什么不应该在循环中进行异步调用。你可能想检查一下。我的linkedin用户名是robertleecrabtree以上是关于如何从数组中创建一个在一行中显示多个 UIImageViews 的类?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Google 表格中创建多个工作表引用的 INDIRECT 数组字符串?