我正在尝试使ScrollView
中的内容居中,当该内容足够小而不需要滚动时,而是将其对齐到顶部。这是一个错误还是我缺少添加一些东西?使用Xcode 11.4 (11E146)
@State private var count : Int = 100
var body : some View {
// VStack {
ScrollView {
VStack {
Button(action: {
if self.count > 99 {
self.count = 5
} else {
self.count = 100
}
}) {
Text("CLICK")
}
ForEach(0...count, id: \.self) { no in
Text("entry: \(no)")
}
}
.padding(8)
.border(Color.red)
.frame(alignment: .center)
}
.border(Color.blue)
.padding(8)
// }
}
答案 0 :(得分:3)
您添加的frame(alignment: .center)
修改器不起作用,因为它所做的是将视图包装在大小完全相同的新视图中。因此,对齐不会执行任何操作,因为没有其他空间可以重新放置视图。
一种可能的解决方案是将整个ScrollView
包裹在GeometryReader
中以读取可用高度。然后使用该高度指定子项不应小于该子项。然后,这将使您的视图居于ScrollView
内部。
struct ContentView: View {
@State private var count : Int = 100
var body : some View {
GeometryReader { geometry in
ScrollView {
VStack {
Button(action: {
if self.count > 99 {
self.count = 5
} else {
self.count = 100
}
}) {
Text("CLICK")
}
ForEach(0...self.count, id: \.self) { no in
Text("entry: \(no)")
}
}
.padding(8)
.border(Color.red)
.frame(minHeight: geometry.size.height) // Here we are setting minimum height for the content
}
.border(Color.blue)
}
}
}
答案 1 :(得分:3)
信用去@Thaniel查找解决方案。我的目的是更全面地说明幕后发生的事情,以使SwiftUI神秘化,并说明解决方案为何有效。
将ScrollView
包裹在GeometryReader
内,以便您可以设置可滚动内容的最小高度(如果滚动视图为水平,则为最小宽度)以匹配ScrollView
的高度。这将使可滚动区域的尺寸永远不会小于ScrollView
的尺寸。您还可以声明一个静态尺寸,并使用它来设置ScrollView
及其内容的高度。
@State private var count : Int = 5
var body: some View {
// use GeometryReader to dynamically get the ScrollView height
GeometryReader { geometry in
ScrollView {
VStack(alignment: .leading) {
ForEach(0...self.count, id: \.self) { num in
Text("entry: \(num)")
}
}
.padding(10)
// border is drawn before the height is changed
.border(Color.red)
// match the content height with the ScrollView height and let the VStack center the content
.frame(minHeight: geometry.size.height)
}
.border(Color.blue)
}
}
@State private var count : Int = 5
// set a static height
private let scrollViewHeight: CGFloat = 800
var body: some View {
ScrollView {
VStack(alignment: .leading) {
ForEach(0...self.count, id: \.self) { num in
Text("entry: \(num)")
}
}
.padding(10)
// border is drawn before the height is changed
.border(Color.red)
// match the content height with the ScrollView height and let the VStack center the content
.frame(minHeight: scrollViewHeight)
}
.border(Color.blue)
}
内容的界限似乎小于ScrollView
,如红色边框所示。发生这种情况是因为在绘制边框之后设置了框架。它还说明了内容的默认大小小于ScrollView
的事实。
首先,让我们了解SwiftUI的ScrollView
的工作原理。
ScrollView
将其内容包装在一个名为ScrollViewContentContainer
的子元素中。ScrollViewContentContainer
始终与ScrollView
的顶部或前边缘对齐,这取决于它是否可沿垂直轴或水平轴滚动,或者可同时沿这两个方向滚动。ScrollViewContentContainer
会根据ScrollView
的内容自行调整大小。ScrollView
时,ScrollViewContentContainer
将其推到顶部或前沿。这就是内容居中的原因。
ScrollViewContentContainer
与其父ScrollView
具有相同的宽度和高度。GeometryReader
可用于动态获取ScrollView
的高度,也可声明静态尺寸,以便ScrollView
及其内容可以使用相同的参数来设置其高度水平或垂直尺寸。.frame(minWidth:,minHeight:)
内容上使用ScrollView
方法可确保它永远不会小于ScrollView
。VStack
或HStack
可以使内容居中。ScrollView
的位置,并且ScrollViewContentContainer
保留了其与顶部或前边缘对齐的默认行为。答案 2 :(得分:1)
您只能观察到正常的ScrollView行为。这是实现目标的可能方法的演示。
// view pref to detect internal content height
struct ViewHeightKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
// extension for modifier to detect view height
extension ViewHeightKey: ViewModifier {
func body(content: Content) -> some View {
return content.background(GeometryReader { proxy in
Color.clear.preference(key: Self.self, value: proxy.size.height)
})
}
}
// Modified your view for demo
struct TestAdjustedScrollView: View {
@State private var count : Int = 100
@State private var myHeight: CGFloat? = nil
var body : some View {
GeometryReader { gp in
ScrollView {
VStack {
Button(action: {
if self.count > 99 {
self.count = 5
} else {
self.count = 100
}
}) {
Text("CLICK")
}
ForEach(0...self.count, id: \.self) { no in
Text("entry: \(no)")
}
}
.padding(8)
.border(Color.red)
.frame(alignment: .center)
.modifier(ViewHeightKey()) // read content view height !!
}
.onPreferenceChange(ViewHeightKey.self) {
// handle content view height
self.myHeight = $0 < gp.size.height ? $0 : gp.size.height
}
.frame(height: self.myHeight) // align own height with content
.border(Color.blue)
.padding(8)
}
}
}