【问题标题】:SwiftUI detect when the view is not visible (kinda View will disappear) and stop publishersSwiftUI 检测视图何时不可见(有点视图会消失)并停止发布者
【发布时间】: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

标签: ios swift firebase swiftui publisher


【解决方案1】:

你应该检查他们的行为,但我认为你正在寻找的是一个 .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!")
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 2017-07-25
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多