rav*_*inx 5 scrollview ios swift swiftui
目前我有一个看起来像这样的视图。
struct StatsView: View {
var body: some View {
ScrollView {
Text("Test1")
Text("Test2")
Text("Test3")
}
}
}
Run Code Online (Sandbox Code Playgroud)
这会在滚动视图中呈现一个包含 3 个文本的视图,每当我在屏幕中拖动这些文本中的任何一个时,视图都会移动导致其可滚动,即使这 3 个文本适合屏幕并且有剩余空间。我想要实现的是仅在其内容超过屏幕高度大小时才使 ScrollView 可滚动,否则,我希望视图是静态的并且不移动。我尝试使用 GeometryReader 并将滚动视图框架设置为屏幕宽度和高度,内容也相同,但我继续具有相同的行为,我也尝试设置 minHeight、maxHeight 没有任何运气。
我怎样才能做到这一点?
小智 35
由于某种原因,我无法完成上述任何一项工作,但它确实激励我找到了适合我的情况的解决方案。它不像其他的那么灵活,但可以很容易地适应支持两个滚动轴。
import SwiftUI
struct OverflowContentViewModifier: ViewModifier {
@State private var contentOverflow: Bool = false
func body(content: Content) -> some View {
GeometryReader { geometry in
content
.background(
GeometryReader { contentGeometry in
Color.clear.onAppear {
contentOverflow = contentGeometry.size.height > geometry.size.height
}
}
)
.wrappedInScrollView(when: contentOverflow)
}
}
}
extension View {
@ViewBuilder
func wrappedInScrollView(when condition: Bool) -> some View {
if condition {
ScrollView {
self
}
} else {
self
}
}
}
extension View {
func scrollOnOverflow() -> some View {
modifier(OverflowContentViewModifier())
}
}
Run Code Online (Sandbox Code Playgroud)
用法
VStack {
// Your content
}
.scrollOnOverflow()
Run Code Online (Sandbox Code Playgroud)
pae*_*ebu 25
iOS 16.4 后: 您现在可以使用scrollBounceBehavior(_:axes:)修饰符:
var body: some View {
ScrollView {
// your content
}
.scrollBounceBehavior(.basedOnSize, axes: [.vertical])
}
Run Code Online (Sandbox Code Playgroud)
iOS16 后: 我会使用 ViewThatFits 的模式匹配特性:
var body: some View {
ViewThatFits {
// your content
ScrollView {
// same content
}
}
}
Run Code Online (Sandbox Code Playgroud)
Nik*_*ner 13
我的解决方案不会禁用内容交互性
struct ScrollViewIfNeeded<Content: View>: View {
@ViewBuilder let content: () -> Content
@State private var scrollViewSize: CGSize = .zero
@State private var contentSize: CGSize = .zero
var body: some View {
ScrollView(shouldScroll ? [.vertical] : []) {
content().readSize($contentSize)
}
.readSize($scrollViewSize)
}
private var shouldScroll: Bool {
scrollViewSize.height <= contentSize.height
}
}
struct SizeReaderModifier: ViewModifier {
@Binding var size: CGSize
func body(content: Content) -> some View {
content.background(
GeometryReader { geometry in
Color.clear.onAppear() {
DispatchQueue.main.async {
size = geometry.size
}
}
}
)
}
}
extension View {
func readSize(_ size: Binding<CGSize>) -> some View {
self.modifier(SizeReaderModifier(size: size))
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
struct StatsView: View {
var body: some View {
ScrollViewIfNeeded {
Text("Test1")
Text("Test2")
Text("Test3")
}
}
}
Run Code Online (Sandbox Code Playgroud)
Asp*_*eri 11
这是一个解决方案(使用 Xcode 11.4 / iOS 13.4 测试)
struct StatsView: View {
@State private var fitInScreen = false
var body: some View {
GeometryReader { gp in
ScrollView {
VStack { // container to calculate total height
Text("Test1")
Text("Test2")
Text("Test3")
//ForEach(0..<50) { _ in Text("Test") } // uncomment for test
}
.background(GeometryReader {
// calculate height by consumed background and store in
// view preference
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height) })
}
.onPreferenceChange(ViewHeightKey.self) {
self.fitInScreen = $0 < gp.size.height // << here !!
}
.disabled(self.fitInScreen)
}
}
}
Run Code Online (Sandbox Code Playgroud)
注意: ViewHeightKey首选项键取自我的解决方案
我为这个问题制作了一个更全面的组件,适用于所有类型的轴集:
代码
struct OverflowScrollView<Content>: View where Content : View {
@State private var axes: Axis.Set
private let showsIndicator: Bool
private let content: Content
init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, @ViewBuilder content: @escaping () -> Content) {
self._axes = .init(wrappedValue: axes)
self.showsIndicator = showsIndicators
self.content = content()
}
fileprivate init(scrollView: ScrollView<Content>) {
self._axes = .init(wrappedValue: scrollView.axes)
self.showsIndicator = scrollView.showsIndicators
self.content = scrollView.content
}
public var body: some View {
GeometryReader { geometry in
ScrollView(axes, showsIndicators: showsIndicator) {
content
.background(ContentSizeReader())
.onPreferenceChange(ContentSizeKey.self) {
if $0.height <= geometry.size.height {
axes.remove(.vertical)
}
if $0.width <= geometry.size.width {
axes.remove(.horizontal)
}
}
}
}
}
}
private struct ContentSizeReader: View {
var body: some View {
GeometryReader {
Color.clear
.preference(
key: ContentSizeKey.self,
value: $0.frame(in: .local).size
)
}
}
}
private struct ContentSizeKey: PreferenceKey {
static var defaultValue: CGSize { .zero }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = CGSize(width: value.width+nextValue().width,
height: value.height+nextValue().height)
}
}
// MARK: - Implementation
extension ScrollView {
public func scrollOnlyOnOverflow() -> some View {
OverflowScrollView(scrollView: self)
}
}
Run Code Online (Sandbox Code Playgroud)
用法
ScrollView([.vertical, .horizontal]) {
Text("Ciao")
}
.scrollOnlyOnOverflow()
Run Code Online (Sandbox Code Playgroud)
注意力
此代码在这些情况下无法工作:
小智 5
ScrollView基于 Asperi 的答案,当我们知道内容即将溢出时,我们可以有条件地用 a 包裹视图。这是您可以创建的 View 的扩展:
extension View {
func useScrollView(
when condition: Bool,
showsIndicators: Bool = true
) -> AnyView {
if condition {
return AnyView(
ScrollView(showsIndicators: showsIndicators) {
self
}
)
} else {
return AnyView(self)
}
}
}
Run Code Online (Sandbox Code Playgroud)
在主视图中,只需使用您的逻辑检查视图是否太长,也许使用GeometryReader背景颜色技巧:
struct StatsView: View {
var body: some View {
VStack {
Text("Test1")
Text("Test2")
Text("Test3")
}
.useScrollView(when: <an expression you write to decide if the view fits, maybe using GeometryReader>)
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5130 次 |
| 最近记录: |