我在尝试将SwiftUI视图插入现有UIKit视图时遇到了一个问题。SwiftUI视图可以动态更改高度,但UIStackView无法调整到新的大小。我创建了一个(可怕的)测试项目来突出显示这一点。
按钮:
class TestButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setupButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupButton()
fatalError("init(coder:) has not been implemented")
}
func setupButton() {
setTitleColor(.white, for: .normal)
backgroundColor = .red
titleLabel?.font = .boldSystemFont(ofSize: 25)
layer.cornerRadius = 10
}
}
SwiftUI视图:
struct TestSwiftUIView: View {
@State var text: [String] = ["This is a line of text"]
var body: some View {
VStack {
ForEach(text, id: \.self) { text in
Text(text)
}
Button {
text.append("New line")
} label: {
Text("Add line")
}
.padding()
.background(Color.red)
}
.foregroundColor(.white)
.background(Color.green)
}
}
视图控制器:
class ViewController: UIViewController {
var titleLabel = UILabel()
var stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
configureTitleLabels()
configureStackView()
}
func configureStackView() {
view.addSubview(stackView)
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 20
addButtonsToStackView()
setStackViewConstraints()
let hostingController = UIHostingController(rootView: TestSwiftUIView())
stackView.insertArrangedSubview(hostingController.view, at: 3)
}
func addButtonsToStackView() {
let numberOfButtons = 5
for i in 1...numberOfButtons {
let button = TestButton()
button.setTitle("\(i)", for: .normal)
stackView.addArrangedSubview(button)
}
}
func setStackViewConstraints() {
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 30)
.isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 50).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -50).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30).isActive = true
}
func configureTitleLabels() {
view.addSubview(titleLabel)
titleLabel.text = "Test project"
titleLabel.font = .systemFont(ofSize: 30)
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
titleLabel.adjustsFontSizeToFitWidth = true
setTitleLabelConstaints()
}
func setTitleLabelConstaints() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)
.isActive = true
titleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
}
}
结果如下:
我们如何确保UIStackView为扩展的SwiftUI视图提供足够的空间?
更新
我有一个想法,我可能需要对layoutIfNeeded()
做一些事情。所以我在SwiftUI视图中添加了一个回调函数,如下所示:
let callback: () -> Void
然后在按钮功能中:
Button {
text.append("New line")
callback()
} label: {
Text("Add line")
.font(.system(size: 18, weight: .bold, design: nil))
}
然后在ViewController中:
let hostingController = UIHostingController(rootView: TestSwiftUIView(callback: {
self.stackView.subviews.forEach { view in
view.sizeToFit()
view.layoutIfNeeded()
}
}))
可惜没有影响:(
1条答案
按热度按时间fnvucqvd1#
你遇到了几个问题...实际上,有几个:(
TL;DR版本:
UIHostingController
视图与UIKit
视图的工作方式不同.fillEqually
,这意味着排列好的子视图将永不改变大小。详细版本:
首先,当UIKit加载
UIHostingController
视图时,它使用该视图的.intrinsicContentSize
。但是,由于SwiftUI视图的工作方式,它不会“自动”更新。您可以通过将UIKit视图控制器分解为仅托管控制器来查看此信息:
这是我们得到的:
正如我们所看到的,绿色的
vStack
垂直扩展,但黄色视图没有变化。有几种方法可以“解决”这个问题...
首先,您的
callback
方法是正确的,但是让我们这样做。我们将添加一个var属性来保存对托管视图的引用:
当我们得到回调时,我们会像这样更新它:
下面是一个修改后的示例:
这是输出,黄色视图随着绿色视图沿着增长:
所以--喔喔!只要用你的栈视图布局实现它,我们就完成了,对吗?
不完全是。
您为堆栈视图设置了Top和Bottom约束,并设置了
.distribution = .fillEqually
。所以,堆栈视图说:“我现在有6个排列好的子视图,所以让它们都有相同的高度***,永远不要让高度改变!!!***
你可能会尝试
.distribution = .fillProportionally
...不幸的是,当堆栈视图的间距不为零时,它会失败。有一些方法可以解决这个问题,但它可能不会给予你想要的结果。因此,让我们使用
CallbackTestSwiftUIView
和原始ViewController
的稍微修改版本,并使用堆栈视图和按钮:可能会也可能不会给予你你想要的:
但至少我们在堆栈视图中调整了大小。
一些搜索提出了各种方法来制作“自动调整大小”的托管控制器......我很少使用SwiftUI,所以我不能给予你推荐哪种方法可能是最好的。