获取 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】:

如果您没有找到任何选项。您仍然可以将标准UIScrollViewdelegatesUIViewRepresentable 一起使用,方法是使单独的结构符合它。

您可以在 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 获取的结果

SwiftUI - 如何在运行时更改内部内容后获取 ScrollView 内容高度

带有 ScrollView 外观的 SwiftUI 错误

获取 SwiftUI ScrollView 的当前滚动位置

SwiftUI ScrollView 没有更新?