SwiftUI 高级之水平滚动并获取滚动位置

ScrollView可以实现水平滚动,可能滚动过程中如何获取当前滚动对位置呢。下文给大家提供了一个解决方案。

效果

SwiftUI 高级之水平滚动并获取滚动位置_第1张图片

代码

Page代码

//
//  PagingScrollView.swift
//  SwiftUIPagingScrollView
//
//  Created by myf on 27/08/2019.
//  Copyright © 2019 Pavel Zak. All rights reserved.
//

import SwiftUI

struct PagingScrollView: View {
    let items: [AnyView]

    init(activePageIndex:Binding, itemCount: Int, pageWidth:CGFloat, tileWidth:CGFloat, tilePadding: CGFloat, @ViewBuilder content: () -> A) {
        let views = content()
        self.items = [AnyView(views)]
        
        self._activePageIndex = activePageIndex
        
        self.pageWidth = pageWidth
        self.tileWidth = tileWidth
        self.tilePadding = tilePadding
        self.tileRemain = (pageWidth-tileWidth-2*tilePadding)/2
        self.itemCount = itemCount
        self.contentWidth = (tileWidth+tilePadding)*CGFloat(self.itemCount)
        
        self.leadingOffset = tileRemain+tilePadding
        self.stackOffset = contentWidth/2 - pageWidth/2 - tilePadding/2
    }
    
    /// index of current page 0..N-1
    @Binding var activePageIndex : Int
    
    /// pageWidth==frameWidth used to properly compute offsets
    let pageWidth: CGFloat
    
    /// width of item / tile
    let tileWidth : CGFloat
    
    /// padding between items
    private let tilePadding : CGFloat
    
    /// how much of surrounding iems is still visible
    private let tileRemain : CGFloat
    
    /// total width of conatiner
    private let contentWidth : CGFloat
    
    /// offset to scroll on the first item
    private let leadingOffset : CGFloat
    
    /// since the hstack is centered by default this offset actualy moves it entirely to the left
    private let stackOffset : CGFloat // to fix center alignment
    
    /// number of items; I did not come with the soluion of extracting the right count in initializer
    private let itemCount : Int
    
    /// some damping factor to reduce liveness
    private let scrollDampingFactor: CGFloat = 0.66
    
    /// current offset of all items
    @State var currentScrollOffset: CGFloat = 0
    
    /// drag offset during drag gesture
    @State private var dragOffset : CGFloat = 0
    
    
    func offsetForPageIndex(_ index: Int)->CGFloat {
        let activePageOffset = CGFloat(index)*(tileWidth+tilePadding)
        
        return self.leadingOffset - activePageOffset
    }
    
    func indexPageForOffset(_ offset : CGFloat) -> Int {
        guard self.itemCount>0 else {
            return 0
        }
        let offset = self.logicalScrollOffset(trueOffset: offset)
        let floatIndex = (offset)/(tileWidth+tilePadding)
        var computedIndex = Int(round(floatIndex))
        computedIndex = max(computedIndex, 0)
        return min(computedIndex, self.itemCount-1)
    }
    
    /// current scroll offset applied on items
    func computeCurrentScrollOffset()->CGFloat {
        return self.offsetForPageIndex(self.activePageIndex) + self.dragOffset
    }
    
    /// logical offset startin at 0 for the first item - this makes computing the page index easier
    func logicalScrollOffset(trueOffset: CGFloat)->CGFloat {
        return (trueOffset-leadingOffset) * -1.0
    }
    
   
    var body: some View {
        GeometryReader { outerGeometry in
            HStack(alignment: .center, spacing: self.tilePadding)  {
                /// building items into HStack
                ForEach(0..

界面

import SwiftUI

struct TileView: View {
    
    let icon: String
    var color: Color
    
    var body: some View {
        VStack {
            ZStack {
                Rectangle()
                    .fill(color)
                    .cornerRadius(20.0)
                Image(systemName: icon)
                    .imageScale(.large)
                    .font(.largeTitle)
            }
        }
    }
}

struct ContentView: View {
    @State private var scrollEffectValue: Double = 13
    @State private var activePageIndex: Int = 0
    
    let tileWidth: CGFloat = 220
    let tilePadding: CGFloat = 20
    let numberOfTiles: Int = 10
    var items = [Color.red, Color.orange, Color.yellow, Color.green,         Color.blue, Color.purple,Color.red, Color.orange, Color.yellow, Color.green,         Color.blue, Color.purple]
    
    var body: some View {
        VStack {
            Spacer()
            GeometryReader { geometry in
                PagingScrollView(activePageIndex: self.$activePageIndex, itemCount:self.numberOfTiles ,pageWidth:geometry.size.width, tileWidth:self.tileWidth, tilePadding: self.tilePadding){
                    ForEach(0 ..< self.numberOfTiles) { index in
                        GeometryReader { geometry2 in
                            TileView(icon: "\(index + 1).circle",color:self.items[index])
                               
                                .rotation3DEffect(Angle(degrees: Double((geometry2.frame(in: .global).minX - self.tileWidth*0.5) / -10 )), axis: (x: 2, y: 11, z: 1))
                                .onTapGesture {
                                    print ("tap on index: \(index) current:\(self.$activePageIndex)")
                            }
                        }
                    }
                }
            }.frame(height:300)
            List{
                Text("current:\(self.activePageIndex)")
                Text("current:\(self.items[self.activePageIndex].description)")
            }
            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

源码参考

https://github.com/izakpavel/SwiftUIPagingScrollView

更多SwiftUI教程和代码关注专栏

QQ:3365059189
SwiftUI技术交流QQ群:518696470

https://www.jianshu.com/c/7b3...

你可能感兴趣的:(ios,swift)