SwiftUI 检测视图何时不可见(有点视图会消失)并停止发布者
Posted
技术标签:
【中文标题】SwiftUI 检测视图何时不可见(有点视图会消失)并停止发布者【英文标题】:SwiftUI detect when the view is not visible (kinda View will disappear) and stop publishers 【发布时间】:2021-08-10 22:07:52 【问题描述】:我正在使用 SwiftUI 和 Firestore 开发应用程序。
在主页上,我显示了 Firestore 中的项目列表,这些项目在后端有任何更新时都会正确更新。 现在在顶部加号按钮上,我正在导航到另一个屏幕以添加新项目。 添加新项目时,主页上的列表会更新,即再次将 AddNewItemView 推送到堆栈中。
在这种情况下,我想检测列表屏幕现在是否不可见并停止列出事件。
import SwiftUI
struct HomeView: View
private let useCase: AuthLogoutProvider = AuthService.shared
@ObservedObject var viewModel: HomeViewModel
init(_ viewModel: HomeViewModel)
self.viewModel = viewModel
private var addTeam: some View
return HStack
Text("Add Team +")
.font(.system(size: 20))
.fontWeight(.regular)
.foregroundColor(Color.secondary)
.padding(.vertical)
.frame(width: 150, height: 54)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.secondary, lineWidth: 1)
)
var body: some View
NavigationView
VStack
HStack
NavigationLink(destination: AddTeamInputView(), label:
addTeam
)
Spacer()
.padding()
List(viewModel.teamsInfoModels, id: \.id) teamInfoModel in
VStack
NavigationLink(destination: TeamDetailView(teamInfoModel), label:
EmptyView()
)
TeamInfoCell(teamInfoModel)
Spacer()
.navigationTitle( Text("Countries"))
.navigationBarItems(
trailing: Button(action:
useCase.signOut()
self.viewModel.logoutPerformed()
)
Text("Log out")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.secondary)
)
.onAppear
print("HomeView onAppear")
.onDisappear
print("HomeView onDisappear")
这是我的主页视图,其中包含团队列表和添加团队按钮
点击添加新按钮导航到另一个屏幕以在 Firestore 上添加团队
添加团队输入视图
struct AddTeamInputView: View
@ObservedObject private var viewModel: AddTeamInputViewModel
init(_ viewModel: AddTeamInputViewModel = AddTeamInputViewModel())
self.viewModel = viewModel
print("AddTeamInputView init called")
var body: some View
VStack
HStack
TextField("Enter Team name", text: $viewModel.teamName)
.font(.title2)
.foregroundColor(.secondary)
.frame(width: UIScreen.main.bounds.width - 40)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.secondary, lineWidth: 1)
)
if viewModel.move
NavigationLink(destination: CountrySelectionView(viewModel: CountrySelectionViewModel(viewModel.createdTeam())), isActive: $viewModel.move)
EmptyView()
AppButton(action:
self.viewModel.addTeam()
, title: "Save", width: 200)
.padding(.vertical, 40)
.navigationTitle("Name Your Team!")
添加团队视图模型
class AddTeamInputViewModel: ObservableObject
init()
print("AddTeamInputViewModel init")
@Published var teamName: String = ""
@Published var move: Bool = false
private var team: Team? = nil
func addTeam()
TeamDataProvider.shared.createTeam(teamName) (error, teamId) in
if let teamId = teamId
if let team = TeamDataProvider.shared.team(forId: teamId)
DispatchQueue.main.async
self.team = team
print("Move to select country for team == \(self.team?.teamName ?? "") == \(self.move)")
self.move.toggle()
print("Move Value after toggle == \(self.move)")
else
print("Team issue")
else
print("Team issue")
func createdTeam() -> Team
return team!
deinit
print("AddTeamInputViewModel deinit called")
【问题讨论】:
您没有显示任何代码。假设,您有一些模型来确定将呈现 什么,表示 ListView 的数据为空或 nil。因此,您可能会在“真相之源”(模型)中找到答案。如果没有,请重新考虑您的设计。 如果您的 ListView 被模态视图覆盖,您的模型或 ViewModel 应该知道这一点。如果您的视图被导航堆栈上的另一个视图覆盖,您的模型 (ViewModel) 应该知道这一点。只有少数情况下模型不知道部分覆盖 ListView 的模态视图。在这种情况下,您不应该停止观察事件。 @CouchDeveloper 你能检查给定的代码并提出一些建议吗 我假设您会在 HomeView 中看到团队的更新,因为在 AddTeamInputView 中添加了一个新团队并将其提交给数据提供者。然后从您的 ViewModel 获取团队的 onChange 通知。恕我直言,这就是它应该如何工作的!但是,您可以将 AddTeamInputView 更改为模式视图(工作表),并在那里收集新的团队信息,然后在关闭时,让您的 HomeViewModel 处理“添加新团队”操作。因此,您可能只需要 一个 ViewModel,但需要一个更完整的底层 DataProvider (CRUD) API 和一个为团队提供属性的简单 InputView。 我理解你的意见,但我们对这个流程很严格,因为在添加团队之后还有另一个流程,比如邀请成员加入。这里的流程与您在 HomeView 上观察更改然后在 AddTeamView 上添加新团队的流程相同,该团队为 Homeview 创建另一个事件,然后 HomeView 意外推送 AddTeamView 【参考方案1】:你应该检查他们的行为,但我认为你正在寻找的是一个 .onAppear 和 .onDisappear 视图修饰符,一旦视图出现并相应地消失,它们就会收到回调 有关更多信息,您可以在以下链接中阅读:
onAppear onDisappear【讨论】:
onDisappear 仅在从堆栈中删除视图时调用。这里 ListView 没有从堆栈中删除,但另一个视图被推到它的顶部 只有将这些修饰符放在 navigationView 上才是正确的,尝试将这些修饰符放在视图内的某个项目上,例如 List 或 ForEach 实际上内部视图也没有按预期工作。因为每当状态更新时,那些出现和消失的块不会在实际视图上被调用,会出现并消失【参考方案2】:这不是一个答案,而是一个示例,到目前为止,最小实现没有问题:
import SwiftUI
struct InputView: View
@State var name: String
let submitOnDisappear: Bool
let onSubmit: (String) -> Void
init(name: String, onSubmit: @escaping (String) -> Void, submitOnDisappear: Bool = false)
self.name = name
self.submitOnDisappear = submitOnDisappear
self.onSubmit = onSubmit
var body: some View
Form
TextField("Name", text: $name)
.onSubmit
if submitOnDisappear == false
onSubmit(name)
.onDisappear
if submitOnDisappear
onSubmit(name)
struct DetailView: View
let detail: String
var body: some View
Text(detail)
struct ListView: View
let items: [String]
let addNewItem: (String) -> Void
enum Sheet: Int, Identifiable
case new
var id: Int self.rawValue
@State private var showSheet: Sheet? = nil
var body: some View
VStack
Button(action: showSheet = .new )
Text("New item via Sheet")
NavigationLink(
destination: InputView(name: "", onSubmit: addNewItem, submitOnDisappear: true),
label: Text("New item via push")
)
List(items, id: \.self) item in
NavigationLink(destination: DetailView(detail: item),
label: Text(item) )
.sheet(item: $showSheet) sheet in
switch sheet
case .new:
InputView(name: "",
onSubmit: value in
showSheet = nil
addNewItem(value)
)
class ViewModel: ObservableObject
struct ViewState
var items: [String] = []
@Published private(set) var viewState = ViewState()
init()
self.viewState.items = ["John"]
func add(item: String)
self.viewState.items.append(item)
struct ContentView: View
@StateObject var viewModel = ViewModel()
var body: some View
ListView(items: viewModel.viewState.items,
addNewItem: viewModel.add(item:))
.listStyle(.plain)
.navigationTitle("Items")
import PlaygroundSupport
PlaygroundPage.current.setLiveView(
NavigationView
ContentView()
.navigationViewStyle(.stack)
)
通过视图推送向导航堆栈添加新项目不适合我。它看起来和行为都很尴尬。从用户体验的角度来看,它应该是一个模态视图。
【讨论】:
感谢您的努力,但如果您能在 git 上提供帮助,我们将无法更改流程,我们将不胜感激【参考方案3】:伙计们,我已经通过将 ObservableObject 更改为 StateObject 解决了这个问题,尽管仍然不知道为什么会发生这种情况。 我只是为了依赖注入而这样做
struct AddTeamInputView: View
@StateObject private var viewModel = AddTeamInputViewModel()
init(_ viewModel)
print("AddTeamInputView init called")
var body: some View
VStack
HStack
TextField("Enter Team name", text: $viewModel.teamName)
.font(.title2)
.foregroundColor(.secondary)
.frame(width: UIScreen.main.bounds.width - 40)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.secondary, lineWidth: 1)
)
if viewModel.move
NavigationLink(destination: CountrySelectionView(viewModel: CountrySelectionViewModel(viewModel.createdTeam())), isActive: $viewModel.move)
EmptyView()
AppButton(action:
self.viewModel.addTeam()
, title: "Save", width: 200)
.padding(.vertical, 40)
.navigationTitle("Name Your Team!")
【讨论】:
以上是关于SwiftUI 检测视图何时不可见(有点视图会消失)并停止发布者的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI - ScrollView 的宽度为 0,我的内容不可见
如何检测 iPhone MPMoviePlayer 控件何时出现/消失?