Двунаправленный бесконечный просмотр страницы в SwiftUI

Я пытаюсь создать двунаправленный 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))
    }
}

В этом случае, когда вы пытаетесь перейти на следующую или предыдущую страницу, анимация прерывается, и исходная страница мгновенно возвращается на свое место.

Можно ли реализовать это именно с помощью TabView, без помощи UIKit и сторонних фреймворков?

Я предполагаю, что одним из возможных решений является изменение источника данных после завершения анимации перехода, но я не смог найти способ сделать это в SwiftUI.
Верно ли мое предположение? Возможно, есть другие, более элегантные способы реализации этой функциональности, и я ошибаюсь.


person Asio Otus    schedule 26.12.2020    source источник
comment
SwiftUI состоит из множества готовых Views, если вы хотите его настроить, лучше использовать одну из форм UIKit того же View. У Apple есть руководства по SwiftUI, в которых рассказывается, как сделать что-то похожее на то, что вы ищете. developer.apple.com/tutorials/swiftui/interfacing-with-uikit   -  person lorem ipsum    schedule 26.12.2020
comment
Это может вам помочь: Как я могу реализовать PageView в SwiftUI?   -  person pawello2222    schedule 26.12.2020
comment
lorem ipsum, pawelio2222, спасибо за комментарии. Я уже пытался реализовать примеры из этих ссылок (и у меня не получилось, попробую еще раз позже). В итоге мне удалось реализовать это с помощью фреймворка SwiftUIPager. Теперь мне интересно, можно ли реализовать описанную задачу на чистом TabView.   -  person Asio Otus    schedule 26.12.2020


Ответы (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)
    }
}

В документации используется onPageChanged для обновления источника данных, но вставка значения в середину или начало массива приведет к сбою приложения.

person Asio Otus    schedule 26.12.2020