SwiftUI 中的双向无限 PageView

Posted

技术标签:

【中文标题】SwiftUI 中的双向无限 PageView【英文标题】:Bidirectional infinite PageView in SwiftUI 【发布时间】:2020-12-26 14:56:28 【问题描述】:

我正在尝试制作一个双向 TabView(使用 .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))),其数据源会随着时间而改变。

下面是描述预期结果的代码:

class TabsViewModel: ObservableObject 
    @Published var items = [-2, -1, 0, 1, 2]
    @Published var selectedItem = 2 
        didSet 
            if let index = items.firstIndex(of: selectedItem), index >= items.count - 2 
                items = items + [items.last! + 1]
                items.removeFirst()
            
            
            if let index = items.firstIndex(of: selectedItem), index <= 1 
                items = [items.first! - 1] + items
                items.removeLast()
            
        
    


struct TabsView: View 
    @StateObject var vm = TabsViewModel()
    
    var body: some View 
        TabView(selection: $vm.selectedItem) 
            ForEach(vm.items, id: \.self)  item in
                Text(item.description)
                    .frame(minWidth: 0,
                           maxWidth: .infinity,
                           minHeight: 0,
                           maxHeight: .infinity,
                           alignment: .topLeading
                    )
                    .background(Color(hue: .random(in: 0...0.99), saturation: .random(in: 0...0.99), brightness: 0.5))
            
        
        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
    

在这种情况下,当您尝试转到下一页或上一页时,动画会中断,原始页面会立即返回原位。

是否可以在不借助 UIKit 和第三方框架的情况下使用 TabView 完全实现这一点?

我的猜测是可能的解决方案之一是在过渡动画完成时更改数据源,但我在 SwiftUI 中找不到这样做的方法。 我的猜测正确吗?也许还有其他更优雅的方式来实现这个功能,而我的想法是错误的。

【问题讨论】:

SwiftUI 由很多开箱即用的Views 组成,如果你想自定义它,最好使用相同View 的UIKit 形式之一。 Apple 有 SwiftUI 教程,它们会指导您如何制作与您正在寻找的东西相似的东西。 developer.apple.com/tutorials/swiftui/interfacing-with-uikit 这可能对你有帮助:How can I implement PageView in SwiftUI? lorem ipsum,pawelio2222,感谢您的 cmets。我已经尝试从这个链接实现示例(我失败了,稍后再试)。最后我设法用 SwiftUIPager 框架实现了它。现在我想知道是否可以使用“纯”TabView 来实现所描述的任务。 【参考方案1】:

任务本身可以使用SwiftUIPager 框架来解决。

import SwiftUI
import SwiftUIPager

class PagerConteinerViewModel: ObservableObject 
    @Published var items = [-3, -2, -1, 0, 1, 2, 3]
    @Published var selectedItemIndex = 3
    
    func updateItems (_ direction: Double) 
        withAnimation 
            if direction > 0 && selectedItemIndex >= items.count - 3 
                items = items + [items.last! + 1]
                items.removeFirst()
            
            
            if direction < 0 && selectedItemIndex <= 2 
                items = [items.first! - 1] + items
                items.removeLast()
            
        
    


struct PagerContainerView: View 
    @StateObject var vm = PagerConteinerViewModel()
    
    var body: some View 
        Pager(page: $vm.selectedItemIndex, data: vm.items,  id: \.self)  item in
            Text("\(item)")
                .frame(minWidth: 0,
                       maxWidth: .infinity,
                       minHeight: 0,
                       maxHeight: .infinity,
                       alignment: .topLeading
                )
                .background(Color(hue: .random(in: 0...0.99), saturation: .random(in: 0...0.99), brightness: 0.5))
        
        .onDraggingEnded(vm.updateItems)
    

文档usesonPageChanged 更新数据源,但随后在数组的中间或开头插入值会使应用程序崩溃。

【讨论】:

以上是关于SwiftUI 中的双向无限 PageView的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 双向绑定到枚举案例中 ObservableObject 中的值

SwiftUI 无限 TabView 项

如何在溢出屏幕的 SwiftUI 中制作无限宽度的视图?

SwiftUI/Combine 发布者是双向的吗?

SwiftUI 如何使圆形动画双向工作

选择器内的 SwiftUI 无限循环