【发布时间】:2019-06-08 03:43:52
【问题描述】:
我看到了 position 属性,但我认为它只是用来设置 x 和 y,但我不知道如何识别当前位置。
或者完全如何使用onHover之类的事件收入属性?
【问题讨论】:
我看到了 position 属性,但我认为它只是用来设置 x 和 y,但我不知道如何识别当前位置。
或者完全如何使用onHover之类的事件收入属性?
【问题讨论】:
见What is Geometry Reader in SwiftUI?,特别是关于GeometryGetter的讨论。如果您将GeometryGetter 放在ScrollView 内容的顶部,它将使用您传递给它的绑定发出其帧。此帧的原点将是滚动视图的负内容偏移量。
【讨论】:
在下面的示例中,您将看到如何使用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)
}
}
```
【讨论】:
您可以通过@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())
}
}
【讨论】:
如果您没有找到任何选项。您仍然可以将标准UIScrollView 与delegates 与UIViewRepresentable 一起使用,方法是使单独的结构符合它。
您可以在 SwiftUI 教程中找到更多详细信息: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit
【讨论】:
我为此创建了 SwiftPackage(纯 SwiftUI ?)
通过这个库,你可以得到ScrollView的位置。
https://github.com/kazuooooo/PositionScrollView
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")
}
}
【讨论】: