获取SwiftUI中ScrollView的当前位置?

时间:2019-06-08 03:43:52

标签: swift swiftui

我看到了position属性,但是我认为它只是用于设置x和y,但是我不知道如何识别当前位置。

还是完全使用onHover这样的事件收益属性?

5 个答案:

答案 0 :(得分:3)

您可以通过@maxnatchanon使用TrackableScrollView

代码如下:

//
//  TrackableScrollView.swift
//  ToutiaoARDemo
//
//  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())
    }
}

答案 1 :(得分:1)

请参见What is Geometry Reader in SwiftUI?,尤其是有关GeometryGetter的讨论。如果将GeometryGetter放在内容ScrollView的顶部,它将使用传递给它的绑定来发出其框架。该帧的原点将是滚动视图的负内容偏移量。

答案 2 :(得分:0)

如果找不到任何选项。您仍然可以将标准UIScrollView与它们的delegatesUIViewRepresentable配合使用,只要使一个单独的结构符合标准即可。

您可以在SwiftUI教程中找到的更多详细信息: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

答案 3 :(得分:0)

在下面的示例中,您将看到如何使用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)
    }
}
```

答案 4 :(得分:0)

我为此目的创建了SwiftPackage(纯SwiftUI?)

有了这个库,您可以获得ScrollView的位置。

https://github.com/kazuooooo/PositionScrollView

Medium post

let maxNumberOfFrames = AVAudioFrameCount(1024)
let sampleRate = 44_100.0
guard let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 2, interleaved: false) else { return nil }
try engine.enableManualRenderingMode(.offline, format: format, maximumFrameCount: maxNumberOfFrames)
if delegate!.newRenderInstrument(connectedTo: engine.mainMixerNode) == nil { return nil }
engine.mainMixerNode.outputVolume = 2.0

sequencer = AVAudioSequencer(audioEngine: engine)
try sequencer!.load(from: data, options: AVMusicSequenceLoadOptions())
sequencer!.prepareToPlay()
let outputURL = folder.appendingPathComponent("\(name).m4a")

let fileSettings: [String: Any] = [
    AVFormatIDKey: NSNumber(value: kAudioFormatMPEG4AAC),
    AVSampleRateKey: NSNumber(value: sampleRate),
    AVNumberOfChannelsKey: NSNumber(value: 2)
]
let outputFile = try AVAudioFile(forWriting: outputURL, settings: fileSettings)

guard let buffer = AVAudioPCMBuffer(pcmFormat: engine.manualRenderingFormat, frameCapacity: engine.manualRenderingMaximumFrameCount) else { return nil }
try engine.start()
try sequencer!.start()