[Swift] 检测 SwiftUI ScrollView 中的偏移量

   2023-03-08 学习力0
核心提示:首先你可以用ScrollViewReader做一些可以从iOS14使用的事情。但是,我不能做我想做的事情,所以我想我还能做些什么。再次,可重复使用我尝试过了。执行我将首先发布实现的图像。 (Swift Playgrounds 演示)您可以像这样根据滚动获取坐标。让我们看看实现。1.

首先

你可以用ScrollViewReader做一些可以从iOS14使用的事情。
但是,我不能做我想做的事情,所以我想我还能做些什么。
再次,可重复使用我尝试过了。

执行

我将首先发布实现的图像。 (Swift Playgrounds 演示)

[Swift] SwiftUI の ScrollView で offset を検知する

您可以像这样根据滚动获取坐标。
让我们看看实现。

1. 在 GeometryReader 中获取偏移量

这种方法本身在很多地方都有描述,所以没有什么特别难的或新的。

创建PreferenceKey 并使用preference(key:,value:) 定位它。它是一种以onPreferenceChange(,perform:) 获取值的形式。

[示例代码]

。迅速
struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value += nextValue()
    }
}
。迅速
ScrollView() {
    GeometryReader { geometry in
        LazyVStack {
            ForEach(0..<100, id: .self) { y in
                Text("(y)")
            }
        }
        .preference(
            key: OffsetPreferenceKey.self, 
            value: $0.frame(in: .global).origin.y
        )
    }
}
.onPreferenceChange(OffsetPreferenceKey.self) { offset in
    print(offset)
}

2.设置为背景

“1. 使用 GeometryReader 获取偏移量”很好,但是跨层次的实现即使是泛化的也不容易使用是。
我想像modifier一样使用它,所以我将它设置为background(),这样我就可以将它分开。

[示例代码]

。迅速
ScrollView() {
    LazyVStack {
        ForEach(0..<100, id: .self) { y in
            Text("(y)")
        }
    }
    .background(GeometryReader {
        Color.clear.preference(
            key: OffsetPreferenceKey.self, 
            value: $0.frame(in: .global).origin.y
        )
    })
}
.onPreferenceChange(OffsetPreferenceKey.self) { offset in
    print(offset)
}

在不影响屏幕的情况下使用Color.clear获取值。
EmptyView 不起作用)

3.同时监控x轴和y轴

目前,需要分别监控 x 轴和 y 轴,同时实现这两个轴会使代码变得更长、更冗长。因此,您可以同时监控两者。

首先,更改PreferenceKey 的设置。

。迅速
struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue = CGPoint.zero // CGPoint に変更
    
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value.x += nextValue().x
        value.y += nextValue().y
    }
}

现在我们有了坐标。接下来,将接收方也修改为CGPoint

对 CGPoint 的更改
- $0.frame(in: .global).origin.y
+ $0.frame(in: .global).origin

现在你可以两者兼得。
(*示例中只有垂直滚动,所以X轴值没有变化)
[Swift] SwiftUI の ScrollView で offset を検知する

如您所见,如果保持原样,则该值会随着每次滚动而增加为负值,因此我们将PreferenceKey稍作修改。

。迅速
struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue = CGPoint.zero // CGPoint に変更
    
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value.x += nextValue().x
        value.y += nextValue().y
        value.x = -value.x // 追加分
        value.y = -value.y // 追加分
    }
}

通过反转返回值,我们得到的值为正值将会
(如果有更好的方法,请告诉我......哈哈)

4.隐藏在自己的ScrollView中

如果在View 直接在ScrollView 下没有使用,参考值会改变。所以要小心。

因此,您可以通过将自己的View 包装在ScrollView 中来在一定程度上绑定实现。

。迅速
struct ScrollWrapperView<Content: View>: View {
    let axes: Axis.Set
    let showsIndicators: Bool
    @ViewBuilder var content: Content
    let perform: (CGPoint) -> Void
    
    init(
        axes: Axis.Set = .vertical, 
        showsIndicators: Bool = true,
        @ViewBuilder content: () -> Content,
        perform: @escaping (CGPoint) -> Void
    ) {
        self.axes = axes
        self.showsIndicators = showsIndicators
        self.content = content()
        self.perform = perform
    }
    
    var body: some View {
        ScrollView(axes, showsIndicators: showsIndicators, content: {
            content
                .background(GeometryReader {
                    Color.clear.preference(
                        key: OffsetPreferenceKey.self, 
                        value: $0.frame(in: .global).origin
                    )
                })
                .onPreferenceChange(
                    OffsetPreferenceKey.self,
                    perform: perform
                )
        })
    }
}

然后你可以像这样使用它:

。迅速
ScrollWrapperView(content: {
    LazyVStack {
        ForEach(0..<100, id: .self) { y in
            Text("(y)")
        }
    }
}, perform: {
    print($0)
})

5. 分离成扩展(奖励)

View extension 易于使用,因此您可以在任何地方使用它。

但是,如前所述,如果不直接在ScrollView下使用View参考值会改变。因此,用户方面需要谨慎。

。迅速
extension View {
    
    func onChangeParentScrollViewOffset(perform: @escaping (CGPoint) -> Void) -> some View {
        self
            .background(GeometryReader {
                Color.clear.preference(
                    key: OffsetPreferenceKey.self, 
                    value: $0.frame(in: .global).origin
                )
            })
            .onPreferenceChange(
                OffsetPreferenceKey.self,
                perform: perform
            )
    }
}

然后你可以像这样使用它:

。迅速
ScrollView {
    LazyVStack {
        ForEach(0..<100, id: .self) { y in
            Text("(y)")
        }
    }
    .onChangeParentScrollViewOffset {
        print($0)
    }
}

就个人而言,我建议使用类似“4. 将其隐藏在您自己的 ScrollView 中”之类的内容,因为您可以在某种程度上实现它。

其他

.global 指定可以从GeometryReader 获取的帧。通过给它一个专有名称,就可以与它自己的坐标系建立关系。

对 CGPoint 的更改
- $0.frame(in: .global).origin.y
+ $0.frame(in: .named("nameSpace")).origin

指定目标的coordinateSpaceView

。迅速
Text("target")
    .coordinateSpace(name: "nameSpace")

有时它只是不起作用,对吗? (有些部分的可用性尚未正确理解),所以你需要了解coordinateSpace

此外,到目前为止的实现不仅可以得到ScrollView,还可以得到List等。

在最后

如果你是这么轻的说唱歌手,维护成本低我认为这很容易处理。

它比较容易使用,所以请务必尝试使用它mm


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628560.html

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • swift 命令行工具初探
    亲爱的同学们好,今天我们要介绍这么一个东西。相信有过解释型语言(PHP,Ruby,等)使用经验的同学会更加熟悉,就是 Swift 也为我们提供了命令行运行工具,俗称 REPL。好了,我们进入正题,在安装好 Swift 开发环境的机器上,打开命令行,输入 swift 命令,就进
    03-16
  • [Swift]冒泡排序 | Bubble sort
    [Swift]冒泡排序 | Bubble sort
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址:https://github.com/strengthen/LeetCode➤原文
    03-08
  • [Swift] 自定义在 SwiftUI 中实现的可搜索的外观
    [Swift] 自定义在 SwiftUI 中实现的可搜索的外
    首先我找遍了,似乎找不到任何信息......(我遇到了许多根本不起作用的事情......)终于在详细的英文文献中找到了,我会保留下来,希望以后有机会。关于 SwiftUI 中的可搜索searchable是iOS15新增的易于实现的搜索字段。关于这种情况有一个参数placement,您
    03-08
  • Swift3.0 UICollectionView 删除,拖动
    Swift3.0 UICollectionView 删除,拖动
    UICollectionView实现了一下常见的新闻分类.  附有效果图 近期一直在深入学习swift,实现了CollectionView item的头东与删除,用的都是系统的一些函数方法,看起来比较直观. 第一步:class HotViewController: UIViewController,UICollectionViewDelegate,UICo
    02-09
  • swift -懒加载创建view
     // 只有外界访问到headerView的时候才会去执行闭包, 然后将闭包的返回值赋值给headerView    // 注意: 一定要记住闭包后面需要写上(), 代表执行闭包    //懒加载创建UIView    lazy var headerView: UIView = {        let view = UIView()
    02-09
  • Swift--非常好用的适合局部的代码约束
    // 哪个控件的哪个属性等于(大于、小于)另外一个控件的哪个属性乘以多少再加上多少 eg:let widthContraint = NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLa
    02-09
  • iOS打包framework - Swift完整项目打包Framework,嵌入OC项目使用
    iOS打包framework - Swift完整项目打包Framewor
    场景说明:-之前做的App,使用Swift框架语言,混合编程,内含少部分OC代码。-需要App整体功能打包成静态库,完整移植到另一个App使用,该App使用OC。-所以涉及到一个语言互转的处理,以及一些AppDelegate的代码减除变化。 --------------------------------
    02-09
  • Swift -- 官方文档Swift-Guides的学习笔记
    在经历的一段时间的郁闷之后,我发现感情都是虚伪的,只有代码是真实的(呸)因为看了swift语法之后依然不会用swift,然后我非常作死的跑去看官方文档,就是xcode里自带的help》documentation and API reference其中的swift里的guide这里主要总结一下里面每一
    02-09
  • Swift - 进度条(UIProgressView)的用法
     1,创建进度条1234var progressView=UIProgressView(progressViewStyle:UIProgressViewStyle.Default)progressView.center=self.view.centerprogressView.progress=0.5 //默认进度50%self.view.addSubview(progressView); 2,设置进度,同时有动画效果1p
    02-09
  • [Swift]LeetCode358. 按距离为k隔离重排字符串
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)➤GitHub地址:https://github.com/strengthen/LeetCode➤原文
    02-09
点击排行