純SwiftUI實(shí)現(xiàn)PageView(無UIView轉(zhuǎn)接)

上文實(shí)現(xiàn)了使用UIScrollView轉(zhuǎn)接的PageView,但其性能有些弱

本文將介紹純SwiftUI實(shí)現(xiàn)的PageView


import Foundation
import SwiftUI

// new PageView in Pure SwiftUI

struct PageView<ViewBuilerContent: View>: View {
    
    @Binding var currentPage: CGFloat
    let contentGetter: () -> [ViewBuilerContent]
    
    init(currentPage: Binding<CGFloat>, @MyViewBuilder content: @escaping (() -> [ViewBuilerContent])) {
        _currentPage = currentPage
        contentGetter = content
    }
    
    private class PageCache {
        var size: CGSize = .zero
        var page: Int = 0
        var offset: CGSize = .zero
    }
    @State private var pageCache: PageCache = PageCache()
    @State private var bounceOffset: CGFloat = 0
    @State private var contentOffset: CGSize = .zero {
        didSet {
            if pageCache.size.width > 0 {
                currentPage = (contentOffset.width - bounceOffset) / pageCache.size.width
            }
        }
    }
    
    var body: some View {
        GeometryReader { geo in
            let contents = self.contentGetter()
            let size = geo.size
            
            let drag = DragGesture().onEnded { val in
                bounceOffset = 0
                // 處理左右兩邊、pageEnabled邏輯
                let minOffset: CGFloat = 0
                let maxOffset: CGFloat = pageCache.size.width * CGFloat(pageCache.page - 1)
                if contentOffset.width < minOffset {
                    contentOffset.width = minOffset
                } else if contentOffset.width > maxOffset {
                    contentOffset.width = maxOffset
                } else {
                    let index = Int(contentOffset.width / pageCache.size.width + 0.5)
                    contentOffset.width = pageCache.size.width * CGFloat(index)
                }
                pageCache.offset = contentOffset
            }.onChanged { val in
                // 簡單模擬彈力
                let k: CGFloat = 0.6
                let minOffset: CGFloat = 0
                let maxOffset: CGFloat = pageCache.size.width * CGFloat(pageCache.page - 1)
                if contentOffset.width < minOffset {
                    let delta = abs(minOffset - contentOffset.width)
                    bounceOffset = -delta * k
                } else if contentOffset.width > maxOffset {
                    let delta = abs(maxOffset - contentOffset.width)
                    bounceOffset = delta * k
                }
                
                // 簡單處理滑動(dòng)
                let ch = val.translation
                contentOffset.width = pageCache.offset.width - ch.width
            }
            
            ForEach(0 ..< contents.count) { i in
                contents[i].frame(width: size.width, height: size.height).offset(x: CGFloat(i) * size.width, y: 0)
            }
            .offset(x: bounceOffset - contentOffset.width) // 注意平時(shí)scrollView的contentOffset和這個(gè)offset是相反的
            .animation(.easeInOut(duration: 0.2), value: contentOffset)
            .gesture(drag)
            .onAppear {
                pageCache.size = size
                pageCache.page = contents.count
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容