使用SF Symbol作为图像源时,UICollectionViewCell中的UIImageView随机改变帧

Posted

技术标签:

【中文标题】使用SF Symbol作为图像源时,UICollectionViewCell中的UIImageView随机改变帧【英文标题】:UIImageView in UICollectionViewCell randomly changing frame when using SF Symbol as image source 【发布时间】:2021-02-16 12:28:43 【问题描述】:

我在 UICollectionViewCell 中有一个 UIImageView。我正在使用自动布局将其设置为具有固定宽度和高度的单元格的左上角。

当我在图像视图上设置图像时,从资产目录中的图像,一切都很好。

当我使用 SF Symbols 在图像视图上设置图像时,UIImageView 的框架会随机变化。

Autolayout 将 UIImageView 的框架设置为单元格的左上角,并将宽度和高度设置为 24。使用 Asset Catalog UIImage(named:... 中的图像时会遵守这一点

当我这样做时,框架会随机变化:

let conf = UIImage.SymbolConfiguration(pointSize: 10, weight: .medium, scale: .large)
let image = UIImage(systemName: "doc.fill", withConfiguration: conf)
imageView.image = image

有时单元格会显示如下图像:

其他时间是这样的:

如果我将 UIImageView 的框架打印到控制台,我会看到更改,如下所示:

testImageView: (8.0, 6.666666666666668, 24.0, 27.0)

或者像这样:

testImageView: (8.0, 7.0, 24.0, 26.666666666666664)

所有其他帧在记录时看起来是恒定的,单元格框架是恒定的等等......从 SF Symbol 生成的图像会强制 UIImageView 的框架发生变化。

为什么会发生这种情况,如何正确使用 SF 符号,以便在将 ImageView 的图像设置为从 SF 符号生成的图像时,我在自动布局中设置的框架在运行时保持不变?

【问题讨论】:

【参考方案1】:

SF Symbols 与图像视图配置一起使用非常很古怪。

我不知道我是否将其称为“错误”或只是未记录(或非常模糊记录)的行为。

像这样设置UIImageView.image 属性:

let conf = UIImage.SymbolConfiguration(pointSize: 10, weight: .medium, scale: .large)
let image = UIImage(systemName: "doc.fill", withConfiguration: conf)
imageView.image = image

将改变图像视图的框架! ...无论约束如何。

这是一个明显的例子......

我们从 4 个图像视图开始,限制为 Width:80,Height:equalTo Width。红线被限制在每个图像视图的顶部和底部:

现在,我们设置每个 imageView 的图像,使用符号配置 pointSize(从 10.0 开始)、weight: .mediumscale: 小、中、大,加上底部使用 NO 配置 --img = UIImage(systemName: "doc.fill") .

在 10.0 pointSize 时,我们已经可以看到图像视图帧的变化:

当我们达到 50.0 的点大小时,“奇怪的帧大小”非常明显:

这是一个像素精确的捕获:

这是此示例的代码,以便您可以更详细地检查所有内容:

class SFSymbolsViewController: UIViewController 
    
    var imgViews: [UIImageView] = []
    var labels: [[UILabel]] = []
    
    // this will be incremented with each tap
    var ptSize: CGFloat = 5.0
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        let g = view.safeAreaLayoutGuide
        
        let infoLabel = UILabel()
        infoLabel.translatesAutoresizingMaskIntoConstraints = false
        infoLabel.numberOfLines = 0
        infoLabel.textAlignment = .center
        infoLabel.font = .systemFont(ofSize: 14.0)
        infoLabel.text = "Tap to add images.\nPointSize will start at 10, and each Tap will increment the Point Size by 5.0 and re-generate the images."

        view.addSubview(infoLabel)
        NSLayoutConstraint.activate([
            infoLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            infoLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            infoLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
        ])
        
        var y: CGFloat = 140
        let yInc: CGFloat = 100

        for _ in 1...4 
            let imageView = UIImageView()
            imageView.backgroundColor = .systemYellow
            imageView.contentMode = .scaleAspectFit
            imageView.translatesAutoresizingMaskIntoConstraints = false
            imgViews.append(imageView)
            
            view.addSubview(imageView)
            
            // horizontal "line" views
            let h1 = UIView()
            let h2 = UIView()
            [h1, h2].forEach  v in
                v.translatesAutoresizingMaskIntoConstraints = false
                v.backgroundColor = .red
                view.addSubview(v)
                NSLayoutConstraint.activate([
                    v.heightAnchor.constraint(equalToConstant: 1.0),
                    v.widthAnchor.constraint(equalTo: g.widthAnchor),
                    v.centerXAnchor.constraint(equalTo: g.centerXAnchor),
                ])
            
            
            // info labels
            var imgLabels: [UILabel] = []
            for _ in 1...3 
                let label = UILabel()
                label.font = .systemFont(ofSize: 12.0)
                label.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(label)
                // add label to right of imageView
                NSLayoutConstraint.activate([
                    label.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 12.0),
                    label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -12.0),
                ])
                imgLabels.append(label)
            
            imgLabels[1].text = "cfg:"
            labels.append(imgLabels)
            
            NSLayoutConstraint.activate([
                // image view Top = y, Leading = 20
                //  width = 80, height = width (1:1 ratio)
                imageView.topAnchor.constraint(equalTo: g.topAnchor, constant: y),
                imageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                imageView.widthAnchor.constraint(equalToConstant: 80.0),
                imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
                
                // put a "line" on top and bottom of imageView
                h1.bottomAnchor.constraint(equalTo: imageView.topAnchor),
                h2.topAnchor.constraint(equalTo: imageView.bottomAnchor),
                
                // label y positions
                imgLabels[0].topAnchor.constraint(equalTo: h1.bottomAnchor, constant: 4.0),
                imgLabels[1].centerYAnchor.constraint(equalTo: imageView.centerYAnchor),
                imgLabels[2].bottomAnchor.constraint(equalTo: h2.topAnchor, constant: -4.0),
            ])
            y += yInc
        
        
        let t = UITapGestureRecognizer(target: self, action: #selector(self.setImages(_:)))
        view.addGestureRecognizer(t)
        
    
    
    override func viewDidAppear(_ animated: Bool) 
        super.viewDidAppear(animated)
        updateSizeLabels(true)
    
    
    func updateSizeLabels(_ orig: Bool) -> Void 
        for (l, v) in zip(labels, imgViews) 
            if orig 
                l[0].text = "Orig Frame: \(v.frame)"
            
            l[2].text = "New Frame: \(v.frame)"
        
    
    
    @objc func setImages(_ g: UITapGestureRecognizer) -> Void 
        
        ptSize += 5.0
        
        var cfg: UIImage.SymbolConfiguration!
        var img: UIImage!
        var i: Int = 0
        
        labels[i][1].text = "cfg: \(ptSize) / medium / small"
        cfg = UIImage.SymbolConfiguration(pointSize: ptSize, weight: .medium, scale: .small)
        img = UIImage(systemName: "doc.fill", withConfiguration: cfg)
        
        imgViews[i].image = img
        
        i += 1
        
        labels[i][1].text = "cfg: \(ptSize) / medium / medium"
        cfg = UIImage.SymbolConfiguration(pointSize: ptSize, weight: .medium, scale: .medium)
        img = UIImage(systemName: "doc.fill", withConfiguration: cfg)
        
        imgViews[i].image = img
        
        i += 1
        
        labels[i][1].text = "cfg: \(ptSize) / medium / large"
        cfg = UIImage.SymbolConfiguration(pointSize: ptSize, weight: .medium, scale: .large)
        img = UIImage(systemName: "doc.fill", withConfiguration: cfg)
        
        imgViews[i].image = img
        
        i += 1
        
        labels[i][1].text = "cfg: NO SymbolConfiguration"
        img = UIImage(systemName: "doc.fill")
        
        imgViews[i].image = img

        // update the size labels after UI updates
        DispatchQueue.main.async 
            self.updateSizeLabels(false)
        
    
    

底线:我相信配置选项与字体的配合更直接相关。以这种方式使用 SF 符号时,使用UIImage.SymbolConfiguration 可能会更好(除非您无法获得所需的外观,在这种情况下,您可能需要跳过一些障碍才能获得尺寸/对齐正确)。

【讨论】:

以上是关于使用SF Symbol作为图像源时,UICollectionViewCell中的UIImageView随机改变帧的主要内容,如果未能解决你的问题,请参考以下文章

SF Symbol imageView 不符合pointSize?

在使用 CSS 加载或破坏图像源时操作图像占位符

尝试使用 Attunity 连接到 Teradata 作为源时出错

WPF 使用XML作为绑定源时Xaml注意事项

使用txt文件作为源时的Tidytext unnest_tokens错误

log4j使用kafka作为输出源时死锁