我想让SwiftUI可以使用自定义的滚动视图,这将允许我设置滚动位置。我碰到一半,滚动时滚动视图在状态栏下无法正确流动。
问题看起来像这样:
在测试中发现什么地方出了问题时,我制作了一个基于情节提要的比较项目,该项目完全没有代码,只是一个结构与我的代码完全相同。然后结果看起来像这样,这就是我想要实现的:
似乎根视图控制器视图并没有完全到达顶部,但是我不知道为什么。所有其他自动滚动嵌入魔术都可以正常工作。
这是我的代码,您可以使用Xcode 11中的SwiftUI创建一个新项目,并将其粘贴到ContentView.swift中。
import SwiftUI
import UIKit
class UIScrollableViewController: UIViewController {
let scrollView = UIScrollView()
let contentController: UIViewController
init(contentController: UIViewController) {
self.contentController = contentController
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
// Add child controller
scrollView.addSubview(contentController.view)
addChild(contentController)
let contentView = contentController.view!
// Constrain scroll view
scrollView.translatesAutoresizingMaskIntoConstraints = false;
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
// Constrain child view to scroll view
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// Constrain width of child view to width of self.view, NOT scroll view
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
}
class ScrollableNavigationController: UINavigationController, UINavigationBarDelegate {
}
struct ScrollableNavigationViewController<Content: View>: UIViewControllerRepresentable {
typealias UIViewControllerType = ScrollableNavigationController
let hostingController: UIHostingController<Content>
let scrollableContainer: UIScrollableViewController!
init(@ViewBuilder content: () -> Content) {
hostingController = UIHostingController(rootView: content())
scrollableContainer = UIScrollableViewController(contentController: hostingController)
scrollableContainer.title = "Scrollable"
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ScrollableNavigationViewController>) -> ScrollableNavigationController {
let navigationController = ScrollableNavigationController(rootViewController: scrollableContainer)
navigationController.navigationBar.prefersLargeTitles = true
return navigationController
}
func updateUIViewController(_ navigationController: ScrollableNavigationController, context: UIViewControllerRepresentableContext<ScrollableNavigationViewController>) {
}
}
struct ScrollableNavigationView<Content: View>: View {
var content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
ScrollableNavigationViewController {
content
}
}
}
// MARK: Test
struct BoxView: View {
var colors: [Color] = [.red, .blue, .orange, .pink, .yellow]
var body: some View {
ForEach(0 ..< 20) { i in
ZStack {
Rectangle()
.fill(self.colors[i % 5])
.padding()
.frame(height: 130)
Text("\(i)")
.font(.title)
}
}
}
}
struct ContentView: View {
var body: some View {
ScrollableNavigationView {
BoxView()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
答案 0 :(得分:0)
问题似乎是场景委托,它导致整个视图层次结构包裹在UIHostingController
中,这是嵌套SwiftUI控制器似乎能够避免的自身插入魔术。为了证明这一点,我想出了一个有点棘手的解决方案。
替换此:
window.rootViewController = UIHostingController(rootView: ContentView())
具有:
window.rootViewController = MyHostingController(rootView: ContentView())
MyHostingController
看起来像这样:
class MyHostingController<Content: View>: UIHostingController<Content> {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.subviews[0].frame = UIScreen.main.bounds
}
}
从iOS 13.1开始,看来您真的应该只尝试在两层UIKit之间集成SwiftUI,而不是相反。 SwiftUI的内部包含太多未记录的魔术。