获取 SwiftUI 中 ScrollView 的当前位置?
Posted
技术标签:
【中文标题】获取 SwiftUI 中 ScrollView 的当前位置?【英文标题】:Get the current position of ScrollView in SwiftUI? 【发布时间】:2019-06-08 03:43:52 【问题描述】:我看到了 position 属性,但我认为它只是用来设置 x 和 y,但我不知道如何识别当前位置。
或者完全如何使用onHover之类的事件收入属性?
【问题讨论】:
【参考方案1】:见What is Geometry Reader in SwiftUI?,特别是关于GeometryGetter
的讨论。如果您将GeometryGetter
放在ScrollView
内容的顶部,它将使用您传递给它的绑定发出其帧。此帧的原点将是滚动视图的负内容偏移量。
【讨论】:
【参考方案2】:在下面的示例中,您将看到如何使用GeometryReader
获取滚动视图中内容的水平位置。但是,我还没有成功找出如何设置滚动位置。 (Xcode 11.0 beta 6 (11M392q))
struct TimelineView: View
@State private var posX: CGFloat = 0
var body: some View
GeometryReader geo in
VStack
Text("\(self.posX)")
ScrollView(.horizontal, showsIndicators: true)
VStack
GeometryReader innerGeo -> Text in
self.posX = innerGeo.frame(in: .global).minX
return Text("")
TimelineGridView()
.position(x: geo.size.width / 2, y: geo.size.height / 2)
地点:
struct TimelineGridView: View
var body: some View
VStack
ForEach(0...10, id: \.self) rowIndex in
TimelineRowView()
struct TimelineRowView: View
var body: some View
HStack
ForEach(0...100, id: \.self) itemIndex in
TimelineCellView()
struct TimelineCellView: View
var body: some View
Rectangle()
.fill(Color.yellow)
.opacity(0.5)
.frame(width: 10, height: 10, alignment: .bottomLeading)
```
【讨论】:
适用于检测 Swift 5 中的滚动视图位置【参考方案3】:您可以通过@maxnatchanon 使用TrackableScrollView
代码如下:
//
// TrackableScrollView.swift
// TrackableScrollView
//
// Created by Frad LEE on 2020/6/21.
// Copyright © 2020 Frad LEE. All rights reserved.
//
import SwiftUI
/// A trackable and scrollable view. Read [this link](https://medium.com/@maxnatchanon/swiftui-how-to-get-content-offset-from-scrollview-5ce1f84603ec) for more.
///
/// The trackable scroll view displays its content within the trackable scrollable content region.
///
/// # Usage
///
/// ``` swift
/// struct ContentView: View
/// @State private var scrollViewContentOffset = CGFloat(0) // Content offset available to use
///
/// var body: some View
/// TrackableScrollView(.vertical, showIndicators: false, contentOffset: $scrollViewContentOffset)
/// ...
///
///
///
/// ```
struct TrackableScrollView<Content>: View where Content: View
let axes: Axis.Set
let showIndicators: Bool
@Binding var contentOffset: CGFloat
let content: Content
/// Creates a new instance that’s scrollable in the direction of the given axis and can show indicators while scrolling.
/// - Parameters:
/// - axes: The scrollable axes of the scroll view.
/// - showIndicators: A value that indicates whether the scroll view displays the scrollable component of the content offset, in a way that’s suitable for the platform.
/// - contentOffset: A value that indicates offset of content.
/// - content: The scroll view’s content.
init(_ axes: Axis.Set = .vertical, showIndicators: Bool = true, contentOffset: Binding<CGFloat>, @ViewBuilder content: () -> Content)
self.axes = axes
self.showIndicators = showIndicators
_contentOffset = contentOffset
self.content = content()
var body: some View
GeometryReader outsideProxy in
ScrollView(self.axes, showsIndicators: self.showIndicators)
ZStack(alignment: self.axes == .vertical ? .top : .leading)
GeometryReader insideProxy in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: [self.calculateContentOffset(fromOutsideProxy: outsideProxy, insideProxy: insideProxy)])
VStack
self.content
.onPreferenceChange(ScrollOffsetPreferenceKey.self) value in
self.contentOffset = value[0]
private func calculateContentOffset(fromOutsideProxy outsideProxy: GeometryProxy, insideProxy: GeometryProxy) -> CGFloat
if axes == .vertical
return outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY
else
return outsideProxy.frame(in: .global).minX - insideProxy.frame(in: .global).minX
struct ScrollOffsetPreferenceKey: PreferenceKey
typealias Value = [CGFloat]
static var defaultValue: [CGFloat] = [0]
static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat])
value.append(contentsOf: nextValue())
【讨论】:
我认为您应该更清楚地表明这不是您的代码,而是从 GitHub 存储库中复制的 - 我知道您已将“by @maxnatchanon”放在顶部,但根本不清楚.我的印象是你写了它,并且正准备在一个项目中把它归功于你。【参考方案4】:如果您没有找到任何选项。您仍然可以将标准UIScrollView
与delegates
与UIViewRepresentable
一起使用,方法是使单独的结构符合它。
您可以在 SwiftUI 教程中找到更多详细信息: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit
【讨论】:
谢谢。我不知道为什么我不能在我不能使用“扩展”的结构中使用这个例子。你能为我的案例写一个示例代码吗?请帮帮我【参考方案5】:我为此创建了 SwiftPackage(纯 SwiftUI ?)
通过这个库,你可以得到ScrollView的位置。
https://github.com/kazuooooo/PositionScrollView
Medium post
import Foundation
import SwiftUI
/// Extended ScrollView which can controll position
public struct MinimalHorizontalExample: View, PositionScrollViewDelegate
/// Page size of Scroll
var pageSize = CGSize(width: 200, height: 300)
// Create PositionScrollViewModel
// (Need to create in parent view to bind the state between this view and PositionScrollView)
@ObservedObject var psViewModel = PositionScrollViewModel(
pageSize: CGSize(width: 200, height: 300),
horizontalScroll: Scroll(
scrollSetting: ScrollSetting(pageCount: 5, afterMoveType: .stickNearestUnitEdge),
pageLength: 200 // Page length of direction
)
)
public var body: some View
return VStack
PositionScrollView(
viewModel: self.psViewModel,
delegate: self
)
HStack(spacing: 0)
ForEach(0...4, id: \.self) i in
ZStack
Rectangle()
.fill(BLUES[i])
.border(Color.black)
.frame(
width: self.pageSize.width, height: self.pageSize.height
)
Text("Page\(i)")
.foregroundColor(Color.white)
.font(.system(size: 24, weight: .heavy, design: .default))
// Get page via scroll object
Text("page: \(self.psViewModel.horizontalScroll?.page ?? 0)")
// Get position via scroll object
Text("position: \(self.psViewModel.horizontalScroll?.position ?? 0)")
struct SampleView_Previews: PreviewProvider
static var previews: some View
return MinimalHorizontalExample()
// Delegate methods of PositionScrollView
// You can monitor changes of position
public func onScrollStart()
print("onScrollStart")
public func onChangePage(page: Int)
print("onChangePage to page: \(page)")
public func onChangePosition(position: CGFloat)
print("position: \(position)")
public func onScrollEnd()
print("onScrollEnd")
【讨论】:
以上是关于获取 SwiftUI 中 ScrollView 的当前位置?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 SwiftUI 获取 ScrollView 的动态文本高度
SwiftUI ScrollView 不显示从 ObservableObject 获取的结果