iOS开发:如何便捷灵活地为UIView设置内在内容尺寸?
(我以Stack Overflow的“自问自答”格式发布此问题,以便该方案易于查找,并获取其他有用观点。)
问题背景场景:
我正在开发一款包含大量UIStackView实例的应用,这些实例用于并排显示自定义图标。
栈视图的子项通常是UILabel,有时则是普通的UIView。
苹果文档指出,UILabel会自行设置内在内容尺寸,多数情况下这一机制运行正常。但部分UILabel实例的内在内容尺寸设置并不理想,导致UIStackView难以实现精准布局。
在另一些场景中,我创建UIView时,并不适合对每个UIView进行子类化,而是在视图创建后向其添加CALayer进行渲染。这种情况下,UIView无法正确设置自身的内在尺寸,因此当它作为UIStackView的子项时,无论如何设置frame,栈视图都会将其内容尺寸识别为(0, 0),即便使用间距和自定义间距选项也无法正常处理。
由于我预先知晓要渲染的图层尺寸,因此希望能像通过UIView(frame: CGRectMake(x, y, w, h))创建视图那样,为UIView预设一个能被UIStackView识别的尺寸(例如内在内容尺寸)。
解决方案
1. 调整UILabel的内在内容尺寸
如果UILabel的默认内在尺寸不符合需求,最直接的方式是给label添加宽高约束强制固定尺寸,这种方式无需子类化,适配性强:
let label = UILabel() label.text = "自定义图标" label.widthAnchor.constraint(equalToConstant: 24).isActive = true label.heightAnchor.constraint(equalToConstant: 24).isActive = true stackView.addArrangedSubview(label)
如果是多行文本的情况,还可以先设置preferredMaxLayoutWidth,再结合约束调整,避免文本换行导致的尺寸异常。
2. 为普通UIView设置可识别的内在尺寸(无需子类化)
对于不需要子类化的UIView,可以通过扩展结合关联对象,动态设置自定义内在尺寸:
import ObjectiveC private var AssociatedObjectKey: UInt8 = 0 extension UIView { var customIntrinsicSize: CGSize? { get { return objc_getAssociatedObject(self, &AssociatedObjectKey) as? CGSize } set { objc_setAssociatedObject(self, &AssociatedObjectKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) invalidateIntrinsicContentSize() } } override open var intrinsicContentSize: CGSize { if let size = customIntrinsicSize { return size } return super.intrinsicContentSize } }
使用时直接给视图赋值即可,UIStackView会自动识别这个尺寸:
let customView = UIView() let layer = CALayer() layer.frame = CGRect(x: 0, y: 0, width: 24, height: 24) layer.backgroundColor = UIColor.red.cgColor customView.layer.addSublayer(layer) // 设置自定义内在尺寸 customView.customIntrinsicSize = CGSize(width: 24, height: 24) stackView.addArrangedSubview(customView)
如果只是临时场景,也可以直接给视图添加宽高约束,效果完全一致:
customView.widthAnchor.constraint(equalToConstant: 24).isActive = true customView.heightAnchor.constraint(equalToConstant: 24).isActive = true
内容的提问来源于stack exchange,提问作者clearlight

