#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
inTestSheetView
и чтение оттуда, или вы передаетеdeepLink
fromContentView
. Дайте мне знать, если вам нужно полное решение.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.