【问题标题】:How to create a class that shows multiple UIImageViews in a line from an Array?如何从数组中创建一个在一行中显示多个 UIImageViews 的类?
【发布时间】: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 函数有什么作用呢?为什么需要关闭?闭包的所有这些参数是什么意思? (不要强迫你寻求帮助的人问你一堆问题,然后必须去寻找你正在使用的框架,阅读它们,然后做一堆工作。编辑你的问题到包括读者理解您的问题所需的所有信息。)

标签: swift uiimageview frontend


【解决方案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
猜你喜欢
  • 1970-01-01
  • 2019-02-03
  • 2023-03-19
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-19
相关资源
最近更新 更多