自定义 UIImageView 不是自定义 UITableViewCell 内的圆圈

Custom UIImageView is not circle inside custom UITableViewCell

我在代码中完成了自定义 UITableViewCell,但是我在使用内部带有 SfSymbol 的圆形 UIImageView 时遇到了问题。有时它运行良好,如您在屏幕截图中所见,但有时它的形状有些奇怪。如果我不设置任何 SfSymbol 形状是好的。

我想我已经尽我所能,但仍然没有用。这是我的自定义单元格代码:

import UIKit

class ListsTableViewCell: UITableViewCell {
    
    // MARK: - Properties
    
    let configuration = UIImage.SymbolConfiguration(pointSize: 16, weight: .medium)
    
    var list: List? {
        didSet {
            guard let list = list else { return }
            
            iconView.backgroundColor = list.color
            titleLabel.text = list.name
        }
    }
    
    // MARK: - Layout properties
    
    var iconView: CircularImageView!
    var titleLabel: UILabel!
    
    // MARK: - Initialization
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        iconView = CircularImageView()
        iconView.translatesAutoresizingMaskIntoConstraints = false
        iconView.tintColor = .white
        iconView.contentMode = .center
        
        titleLabel = UILabel()
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        
        contentView.addSubview(iconView)
        contentView.addSubview(titleLabel)
        
        NSLayoutConstraint.activate([
            iconView.heightAnchor.constraint(equalToConstant: 34),
            iconView.widthAnchor.constraint(equalToConstant: 34),
            iconView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
            iconView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
            iconView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            iconView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
            
            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 12),
            titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

这是 table 行函数的视图单元格:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let list = listsToDisplay![indexPath.row]
    
    let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "list", for: indexPath) as! ListsTableViewCell
    tableViewCell.iconView.image = UIImage(systemName: list.icon, withConfiguration: tableViewCell.configuration)
    tableViewCell.list = list
    tableViewCell.accessoryType = .disclosureIndicator
   
    return tableViewCell
}

这是 UIImageView 的自定义子类

import UIKit

class CircularImageView: UIImageView {
    override func layoutSubviews() {
        self.layer.masksToBounds = true
        self.clipsToBounds = true
        self.layer.cornerRadius = self.frame.size.width / 2
    }
}

移除图像视图的高度限制。而不是使用有助于根据单元格高度占据位置的纵横比。

啊 - 我以前见过这个。

我不知道为什么,但是在 UIImageView 中使用 SF 符号会 改变图像视图的高度!!!

你可以很容易地确认这一点:

  • 在故事板中添加一个视图控制器
  • 添加一个UIImageView
  • 将内容模式设置为居中(并不重要)
  • constrain 宽度:60 高度:60 centerX 和 centerY
  • 检查大小检查器 - 它会显示 60 x 60

现在:

  • 使用下拉菜单将图像设置为“globe”
  • 检查大小检查器 - 它会显示 60 x 59

现在:

  • 使用下拉菜单将图像设置为“信封”
  • 检查大小检查器 - 它会显示 60 x 56.5

没有明显的约束冲突...没有明显的 原因

据我所知(除非随着 iOS 14 发生变化),我们需要将图像视图嵌入 UIView ... 约束它的 centerX 和 centerY ... 设置该视图的背景颜色和圆角半径属性,使其成为圆形。


编辑 -- 作为练习...

两个水平堆栈视图:

  • 对齐:填充
  • 分布:FillEqually
  • 间距:0
  • 每个限制为 Width: 300Height: 100
  • 每个装满3个UIImageViews

给我们 3 100 x 100 个正方形图像视图。对于第二个堆栈,将每个图像视图设置为系统图像,UIImage.SymbolConfiguration(pointSize: 60, weight: .regular)应该还是给我们3个100 x 100方格。

相反,这是结果:

这是生成该代码的代码:

class SystemImageTestViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stack1 = UIStackView()
        stack1.axis = .horizontal
        stack1.alignment = .fill
        stack1.distribution = .fillEqually
        
        let stack2 = UIStackView()
        stack2.axis = .horizontal
        stack2.alignment = .fill
        stack2.distribution = .fillEqually
        
        // add stack views to the view
        [stack1, stack2].forEach {
            [=10=].translatesAutoresizingMaskIntoConstraints = false
            view.addSubview([=10=])
        }
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            stack1.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            stack1.widthAnchor.constraint(equalToConstant: 300.0),
            stack1.heightAnchor.constraint(equalToConstant: 100.0),
            stack1.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            stack2.topAnchor.constraint(equalTo: stack1.bottomAnchor, constant: 20.0),
            stack2.widthAnchor.constraint(equalToConstant: 300.0),
            stack2.heightAnchor.constraint(equalToConstant: 100.0),
            stack2.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
        ])
        
        let colors: [UIColor] = [
            UIColor(red: 0.75, green: 0.00, blue: 0.00, alpha: 1.0),
            UIColor(red: 0.00, green: 0.75, blue: 0.00, alpha: 1.0),
            UIColor(red: 0.00, green: 0.00, blue: 1.00, alpha: 1.0),
        ]
        
        for c in colors {
            let v = UIImageView()
            v.backgroundColor = c
            stack1.addArrangedSubview(v)
        }
        
        let configuration = UIImage.SymbolConfiguration(pointSize: 60, weight: .regular)
        
        let names: [String] = [
            "globe",
            "bandage",
            "envelope",
        ]

        for (c, n) in zip(colors, names) {
            let v = UIImageView()
            v.backgroundColor = c
            v.contentMode = .center
            v.tintColor = .white
            let img = UIImage(systemName: n, withConfiguration: configuration)
            v.image = img
            stack2.addArrangedSubview(v)
        }
        
    }
}

编辑 2 - 只是为了它...

SystemImageViewCircularSystemImageView 实施一对 类:

class SystemImageView: UIView {

    override var contentMode: UIView.ContentMode {
        didSet {
            imageView.contentMode = contentMode
        }
    }
    override var tintColor: UIColor! {
        didSet {
            imageView.tintColor = tintColor
        }
    }
    var image: UIImage = UIImage() {
        didSet {
            imageView.image = image
        }
    }
    let imageView = UIImageView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        clipsToBounds = true
        addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: topAnchor),
            imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
            imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
    }
}

class CircularSystemImageView: SystemImageView {
    override func layoutSubviews() {
        super.layoutSubviews()
        self.layer.masksToBounds = true
        self.clipsToBounds = true
        self.layer.cornerRadius = bounds.size.width * 0.5
    }
}

class SystemImageTestViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stack1 = UIStackView()
        stack1.axis = .horizontal
        stack1.alignment = .fill
        stack1.distribution = .fillEqually
        
        let stack2 = UIStackView()
        stack2.axis = .horizontal
        stack2.alignment = .fill
        stack2.distribution = .fillEqually
        
        let stack3 = UIStackView()
        stack3.axis = .horizontal
        stack3.alignment = .fill
        stack3.distribution = .fillEqually
        
        let stack4 = UIStackView()
        stack4.axis = .horizontal
        stack4.alignment = .fill
        stack4.distribution = .fillEqually
        
        // add stack views to the view
        [stack1, stack2, stack3, stack4].forEach {
            [=11=].translatesAutoresizingMaskIntoConstraints = false
            view.addSubview([=11=])
        }
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            stack1.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            stack1.widthAnchor.constraint(equalToConstant: 300.0),
            stack1.heightAnchor.constraint(equalToConstant: 100.0),
            stack1.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            stack2.topAnchor.constraint(equalTo: stack1.bottomAnchor, constant: 20.0),
            stack2.widthAnchor.constraint(equalToConstant: 300.0),
            stack2.heightAnchor.constraint(equalToConstant: 100.0),
            stack2.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            stack3.topAnchor.constraint(equalTo: stack2.bottomAnchor, constant: 20.0),
            stack3.widthAnchor.constraint(equalToConstant: 300.0),
            stack3.heightAnchor.constraint(equalToConstant: 100.0),
            stack3.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            stack4.topAnchor.constraint(equalTo: stack3.bottomAnchor, constant: 20.0),
            stack4.widthAnchor.constraint(equalToConstant: 300.0),
            stack4.heightAnchor.constraint(equalToConstant: 100.0),
            stack4.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
        ])
        
        let colors: [UIColor] = [
            UIColor(red: 0.75, green: 0.00, blue: 0.00, alpha: 1.0),
            UIColor(red: 0.00, green: 0.75, blue: 0.00, alpha: 1.0),
            UIColor(red: 0.00, green: 0.00, blue: 1.00, alpha: 1.0),
        ]
        
        for c in colors {
            let v = UIImageView()
            v.backgroundColor = c
            stack1.addArrangedSubview(v)
        }
        
        let configuration = UIImage.SymbolConfiguration(pointSize: 60, weight: .regular)
        
        let names: [String] = [
            "globe",
            "bandage",
            "envelope",
        ]

        for (c, n) in zip(colors, names) {
            let v = UIImageView()
            v.backgroundColor = c
            v.contentMode = .center
            v.tintColor = .white
            if let img = UIImage(systemName: n, withConfiguration: configuration) {
                v.image = img
            }
            stack2.addArrangedSubview(v)
        }
        
        for (c, n) in zip(colors, names) {
            let v = SystemImageView()
            v.backgroundColor = c
            v.contentMode = .center
            v.tintColor = .white
            if let img = UIImage(systemName: n, withConfiguration: configuration) {
                v.image = img
            }
            stack3.addArrangedSubview(v)
        }
        
        for (c, n) in zip(colors, names) {
            let v = CircularSystemImageView()
            v.backgroundColor = c
            v.contentMode = .center
            v.tintColor = .white
            if let img = UIImage(systemName: n, withConfiguration: configuration) {
                v.image = img
            }
            stack4.addArrangedSubview(v)
        }
        
    }
}

结果:

或者,用更实用的 let configuration = UIImage.SymbolConfiguration(pointSize: 40, weight: .regular) 来显示尺寸不是由 SF 符号尺寸决定的: