Обмен глубокими ссылками не запускается в таблице SwiftUI

#ios #swift #swiftui

#iOS #swift #swiftui

Вопрос:

У меня проблема с глубокими ссылками в моем приложении SwiftUI.

В моем классе приложения я объявил deepLink как переменную среды для каждого представления в разделе contentView() в иерархии:

 ...

@main
struct TestApp: App {

var userSettings: UserSettings
var dataFetcher: DataFetcher
var dataUpdater: DataUpdater

@State var deepLink = ""

init() {
    userSettings = UserSettings()
    dataFetcher = DataFetcher(userSettings: userSettings)
    dataUpdater = DataUpdater(userSettings: userSettings)
}

var body: some Scene {
    WindowGroup {
        ContentView()
                .environmentObject(userSettings)
                .environmentObject(dataFetcher)
                .environmentObject(dataUpdater)
                .onOpenURL { url in
                    deepLink = url.absoluteString
                }
                .environment(.deepLink, deepLink)
    }
}
  

}

В моем contentView () Я объявил deepLink как переменную среды

 struct ContentView: View {

    ...

    @State var isTestSheetViewPresented = false

    @Environment(.deepLink) var deepLink: String

    ...

    var body: some View {

        Button(action: {
            self.isTestSheetViewPresented = true
        }, label: {
            HStack {
                Spacer()
                Image(systemName: "plus")
                Text("Add")
                Spacer()
            }
        })
        .sheet(isPresented: $isTestSheetViewPresented, content: {
            TestSheetView(isPresented: self.$isTestSheetViewPresented)
        })
        .onChange(of: self.deepLink) { _ in
            self.isTestSheetViewPresented = true
        }
    }

}
  

И TestSheetView выглядит так

 struct TestSheetView: View {

    @Environment(.deepLink) var deepLink: String

    @State var url: String = ""

    var body: some View {
        Text(url)
            .onChange(of: deepLink) { _ in
                if deepLink != "" {
                    self.url = deepLink
                }
            }
    }
  

}

Проблема в том, что когда я нажимаю на ссылку и открывается мое приложение, TestSheetView отображается правильно, но onChange не запускается, если я не прокручиваю немного вниз по листу.

Вместо этого, если я помещу тот же код TestSheetView в contentView, текст будет показан правильно

Комментарии:

1.Похоже, проблема со временем. Во время TestSheetView инициализации файл body создается после deepLink изменения, поэтому он не сможет его обнаружить. Решением может быть использование onAppear in TestSheetView и чтение оттуда, или вы передаете deepLink from ContentView . Дайте мне знать, если вам нужно полное решение.

2. @George_E Ах да, это была проблема со временем. Добавление onAppear () исправило это! Если вы хотите опубликовать ответ, я пометлю его как решение. Спасибо

3. Спасибо — также просто небольшой комментарий, а не deepLink != "" лучше использовать !deepLink.isEmpty . Не только для удобства чтения, но и для производительности, хотя разница не будет значительной.

Ответ №1:

Похоже, проблема со временем. Во время TestSheetView инициализации тело создается после deepLink изменения, поэтому оно не сможет его обнаружить.

Решение состоит в том, чтобы использовать onAppear in TestSheetView и читать оттуда, например:

 struct TestSheetView: View {

    @Environment(.deepLink) var deepLink: String

    @State var url: String = ""

    var body: some View {
        Text(url)
            .onAppear {
                if deepLink != "" {
                    self.url = deepLink
                }
            }
    }
}
  

Ответ №2:

Неэргономично обрабатывать как случай, когда представление еще не появилось, так и случай, когда ссылка перемещается внутри представления. Следующий модификатор представления обрабатывает оба случая. Он предполагает обработчик .onOpenURL() в навигационном представлении верхнего уровня, который устанавливает как текущий выбор вкладки, так и значение среды currentDeepLink.

 struct DeepLinkViewModifier: ViewModifier {
    @Environment(.currentDeepLink) private var currentDeepLink
    let action: ((URL) -> Bool)

    func body(content: Content) -> some View {
        content
            .onAppear {
                if let url = currentDeepLink.wrappedValue,
                    action(url) {
                    currentDeepLink.wrappedValue = nil
                }
            }
            .onOpenURL { url in
                _ = action(url)
            }
    }
}

extension View {
    func onDeepLink(perform action: @escaping ((URL) -> Bool)) -> some View {
        return self.modifier(DeepLinkViewModifier(action: action))
    }
}
  

Использовать:

 struct SomeView: View {
    @State urlString: String = ""

    var body: some View {
        Text(urlString).onDeepLink { url
            self.string = url.absoluteString
            return true // return false if another handler should consume
        }
    }
}

struct ContentView: View {
    @State private var tabSelection: TabSelection = .something
    @State private var currentDeepLink: URL? = nil

    var body: some View {
        TabView(selection: self.$tabSelection) {
        ...
        }
        .onOpenURL { url in
             self.tabSelection = ... // determine selection from URL
             self.currentDeepLink = url
        }
        .environment(.tabSelection, self.$tabSelection)
        .environment(.currentDeepLink, self.$currentDeepLink)
    }
}
  

Комментарии:

1. Спасибо, я попробую. Тем не менее, очень умное решение!

2. К вашему сведению, у меня возникли некоторые проблемы с надежной работой, иногда вызывались оба обработчика. В конце концов я счел, что лучше всего обрабатывать URL-адрес на текущей вкладке. Кроме того, вам необходимо проверить, представлено ли и / или отображается текущее представление, чтобы избежать запуска нескольких обработчиков onOpenURL.