#swiftui
#swiftui
Вопрос:
У меня есть код, который должен делать то же самое -> Создать привязку и обновить представление. Но один работает, а другой нет. Имейте в виду, что это упрощенный пример. Конечная цель — иметь массив разных типов, а затем генерировать представления в зависимости от типа в массиве.
Вот мой код. Его запуск говорит сам за себя. Щелчок по первой метке обновляет представление, а щелчок по второй метке — нет.
struct TestView: View {
@State var objects: [Object1] = [
Object1(integer: 0)
]
var body: some View {
PageViewController(pages: [
VStack(alignment: .leading) {
Text("This text gets updated").underline()
Object1ViewDisplayer(object1: $objects[0]).padding(.bottom)
Text("This text does *not* update. why?").underline()
Object1ViewDisplayer(object1: Binding(get: { objects[0] }, set: { objects[0] = $0}))
}
])
}
}
protocol CommonProtocol {}
struct Object1: CommonProtocol {
var integer: Int
}
struct Object1ViewDisplayer: View {
@Binding var object1: Object1
var body: some View {
Text("[Click here] (object1.integer)")
.onTapGesture {
object1.integer = 1
}
}
}
Чтобы заставить работать pageViewController, я только что скопировал пример Apple. Пожалуйста, также скопируйте его, чтобы заставить приведенный выше код работать.
import SwiftUI
import UIKit
struct PageViewController<Page: View>: UIViewControllerRepresentable {
var pages: [Page]
@State var currentPage: Int = 0
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
guard !context.coordinator.controllers.isEmpty else { return}
pageViewController.setViewControllers(
[context.coordinator.controllers[currentPage]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
var controllers = [UIViewController]()
init(_ pageViewController: PageViewController) {
parent = pageViewController
controllers = parent.pages.map { UIHostingController(rootView: $0) }
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard controllers.count != 1 else { return nil }
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 { return nil }
return controllers[index - 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard controllers.count != 1 else { return nil }
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index 1 == controllers.count { return nil }
return controllers[index 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
if completed,
let visibleViewController = pageViewController.viewControllers?.first,
let index = controllers.firstIndex(of: visibleViewController) {
parent.currentPage = index
}
}
}
}
Почему первая привязка работает, а вторая терпит неудачу? И что еще более важно, как мне заставить вторую работать? Это необходимо для достижения цели, указанной выше.
[РЕДАКТИРОВАТЬ # 1] Кто-то сказал в комментариях, что удаление пользовательской привязки обновит 2-й вид. Это правда, но я думаю, что мне нужно использовать пользовательскую привязку, потому что реальный приведенный выше код в конечном итоге будет следующим:
struct TestView: View {
@State var objects: [CommonProtocol] = [
Object1(integer: 0)
]
var body: some View {
PageViewController(pages: [
VStack(alignment: .leading) {
// Text("This text gets updated").underline()
// Object1ViewDisplayer(object1: $objects[0]).padding(.bottom)
Text("This text does *not* update. why?").underline()
Object1ViewDisplayer(object1: Binding(get: { objects[0] as! Object1}, set: { objects[0] = $0}))
}
])
}
}
objects
Массив будет представлять собой массив типов протоколов (для обеспечения нескольких типов объектов) вместо конкретного типа. И я должен иметь возможность вводить касту протокола обратно в его конкретный тип, как показано в редактировании. Я знаю только, как это сделать с помощью пользовательской привязки. Если есть другой способ, пожалуйста, дайте мне знать.
[Редактировать # 2] Странно то, что если я закомментирую pageViewController, привязка будет работать так, как ожидалось.
struct TestView: View {
@State var objects: [Object1] = [
Object1(integer: 0)
]
var body: some View {
// PageViewController(pages: [
VStack(alignment: .leading) {
// Text("This text gets updated").underline()
// Object1ViewDisplayer(object1: $objects[0]).padding(.bottom)
Text("This text does *not* update. why?").underline()
Object1ViewDisplayer(object1: Binding(get: { objects[0] }, set: { objects[0] = $0}))
}
// ])
}
}
Комментарии:
1. Вы хотите иметь одинаковое значение для обоих объектов?
2. @RajaKishan В реальном приложении будет много разных объектов с разными значениями / типами. Но я упростил этот пример, чтобы напрямую показать ошибку, с которой я столкнулся. Привязка ко второму объекту (привязка, которую я планирую реализовать) не работает. Экран вообще не обновляется, и я не знаю, как это исправить…
3. Я проверил, если вы удалите пользовательскую привязку, она обновит макет.
4. Пожалуйста, добавьте причину для пользовательской привязки.
5. Я обновил вопрос с указанием причины пользовательской привязки.
Ответ №1:
Кто-то сказал мне решение в автономном режиме. Мне нужно обновить код Apple pageViewController следующим образом.
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
guard !context.coordinator.controllers.isEmpty else { return}
for (page, vc) in zip(pages, context.coordinator.controllers) {
let host = vc as! UIHostingController<Page>
host.rootView = page
}
pageViewController.setViewControllers(
[context.coordinator.controllers[currentPage]], direction: .forward, animated: true)
}