我有一个容器堆栈视图,它还有2个其他stackviews,可以将它们分别称为stackview 1和stackview2。Stackview 2有一个带有某些标签的自定义UIView
。
我的问题是与stackview 1,它有2个安排的子视图。此UIStackView
的轴是垂直的。这些子视图是自定义UIView
。这些自定义视图可能包含文本或图像,并且这些自定义视图的高度不是预定义的,它们基于内容(即,基于文本的高度)。
在堆栈视图1中添加了自定义视图1和自定义视图2。在另一个容器堆栈视图中添加了堆栈视图1和堆栈视图2。
我的问题是,即使我为intrinsicContentSize
分发中的这些自定义视图返回了intrinsicContentSize
(基于textview的fillProportionally
),我也无法显示这两个自定义视图(位于stackview 1内)。
如果我为customview返回width
中的常量height
和intrinsicContentSize
,则两个视图都将正确显示。
在我的custom view
中
override var intrinsicContentSize: CGSize {
let size = CGSize.init(width: 100, height: 100)
return size
}
屏幕截图显示了这种行为。
但是,如果我根据
intrinsicContentSize
的UITextView
返回尺寸(customView的子视图,我已禁用textview的滚动),则仅显示customview 2,而不会显示另一个视图(customview 1)。在我的
Custom view
中 override var intrinsicContentSize: CGSize {
let size = textView.intrinsicContentSize
return size
}
屏幕截图显示了这种行为。
我想要
fillProportionally
行为,但无法按我的意愿使其同时显示两个视图(自定义视图1和自定义视图2)。根据文档,
fillProportionally
使用intrinsicContentSize
按比例填充视图。但是,即使我重写了intrinsicContentSize
var,为什么在我的情况下还是不起作用?为什么自定义视图1甚至覆盖其固有内容大小后也仍然具有零高?
我希望这两个自定义视图都显示在堆栈视图1中。
我一直被困在这里,所以我将不胜感激。
最佳答案
(根据我的经验).fillProportionally
的UIStackView
属性是自动布局中最容易理解的元素之一。
因此,我不完全确定这会给您想要的东西,但是请尝试一下。
点击Text
按钮将更改“描述”文本,而点击Height
按钮将更改“容器”视图的高度,因此您可以看到不同数量的文本的外观。Report
按钮将打印得到的视图的高度和比例。
全部通过代码-没有@IBOutlet
或@IBAction
连接-因此,只需从新的视图控制器开始,并将其自定义类分配给ProportionalStackViewController
:
class ProportionalHeightView: UIView {
let myNonScrollTextView: UITextView = {
let v = UITextView()
v.isScrollEnabled = false
v.setContentHuggingPriority(.required, for: .vertical)
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
let padding: CGFloat = 0.0
addSubview(myNonScrollTextView)
myNonScrollTextView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myNonScrollTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
myNonScrollTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
// if we want the text top-aligned
//myNonScrollTextView.topAnchor.constraint(equalTo: topAnchor),
// if we want the text vertically=sentered
myNonScrollTextView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
myNonScrollTextView.sizeToFit()
invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return myNonScrollTextView.bounds.size
}
}
class TitleView: ProportionalHeightView {
override func commonInit() {
super.commonInit()
myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
myNonScrollTextView.backgroundColor = .cyan
backgroundColor = .blue
}
}
class DescView: ProportionalHeightView {
override func commonInit() {
super.commonInit()
myNonScrollTextView.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
myNonScrollTextView.backgroundColor = .yellow
backgroundColor = .orange
}
}
class ProportionalStackViewController: UIViewController {
var titleView: ProportionalHeightView = {
let v = ProportionalHeightView()
v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
v.myNonScrollTextView.backgroundColor = .cyan
v.backgroundColor = .blue
return v
}()
var descView: ProportionalHeightView = {
let v = ProportionalHeightView()
v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 16.0, weight: .regular)
v.myNonScrollTextView.backgroundColor = .yellow
v.backgroundColor = .orange
return v
}()
let containerView: UIView = {
let v = UIView()
v.backgroundColor = .white
return v
}()
let proportionalStackView: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.distribution = .fillProportionally
return v
}()
let changeTextButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Text", for: .normal)
return b
}()
let changeHeightButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Height", for: .normal)
return b
}()
let reportButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Report", for: .normal)
return b
}()
let btnStack: UIStackView = {
let v = UIStackView()
v.distribution = .fillEqually
v.spacing = 20
return v
}()
var nLines = 0
var containerH = NSLayoutConstraint()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemTeal
btnStack.translatesAutoresizingMaskIntoConstraints = false
proportionalStackView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
// add a horizontal stack view with buttons at the top
btnStack.addArrangedSubview(changeTextButton)
btnStack.addArrangedSubview(changeHeightButton)
btnStack.addArrangedSubview(reportButton)
view.addSubview(btnStack)
// set text for titleView
titleView.myNonScrollTextView.text = "Pleasanton Panthers"
descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"
proportionalStackView.addArrangedSubview(titleView)
proportionalStackView.addArrangedSubview(descView)
containerView.addSubview(proportionalStackView)
view.addSubview(containerView)
// respect safe area
let g = view.safeAreaLayoutGuide
containerH = containerView.heightAnchor.constraint(equalToConstant: 240.0)
NSLayoutConstraint.activate([
// buttons stack 20-pts from top / leading / trailing
btnStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btnStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btnStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// container view 20-pts from bottom of buttons, 20-pts from leading / trailing
containerView.topAnchor.constraint(equalTo: btnStack.bottomAnchor, constant: 20.0),
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// container view height
containerH,
// constrain stack view 20-pts from top/bottom/leading/trailing to container
proportionalStackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
proportionalStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),
proportionalStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
proportionalStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),
])
changeTextButton.addTarget(self, action: #selector(changeText), for: .touchUpInside)
changeHeightButton.addTarget(self, action: #selector(changeHeight), for: .touchUpInside)
reportButton.addTarget(self, action: #selector(report), for: .touchUpInside)
[titleView, titleView.myNonScrollTextView, descView, descView.myNonScrollTextView].forEach {
v in
// un-comment next line to clear background colors
//v.backgroundColor = .clear
}
}
@objc func report() -> Void {
let titleTextH = titleView.myNonScrollTextView.frame.size.height
let descTextH = descView.myNonScrollTextView.frame.size.height
let titleViewH = titleView.frame.size.height
let descViewH = descView.frame.size.height
let tRatio = titleTextH / descTextH
let vRatio = titleViewH / descViewH
print("text heights:\t", titleTextH, descTextH)
print("view heights:\t", titleViewH, descViewH)
print("Text view ratio: \(tRatio) view ratio: \(vRatio)")
}
@objc func changeHeight() -> Void {
if containerView.frame.origin.y + containerView.frame.size.height > view.frame.size.height - 20 {
containerH.constant = 220
}
containerH.constant += 20
}
@objc func changeText() -> Void {
nLines += 1
if nLines > 10 {
descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"
nLines = 0
return
}
var s = ""
for i in 1..<nLines {
s += "Line \(i)\n"
}
s += "Line \(nLines)"
descView.myNonScrollTextView.text = s
}
}