You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI标签页视图中如何一致显示视图状态?

问题分析与解决方案

核心问题

两个标签页(关注/粉丝)共用同一个ViewModel的全局state属性,导致状态互相覆盖;同时视图的Equatable实现未包含关键状态/数据,状态变化时视图无法正确刷新,最终出现空白屏幕。


修复步骤

1. 重构ViewModel:拆分独立状态

将原有的单个state拆分为followingState和followerState,分别管理两个标签的加载/完成/空状态,避免互相干扰:

import Foundation
import SwiftUI

class FollowListViewModel: ObservableObject {
    let user: User
    @Published var followingUsers = [User]()
    @Published var followerUsers = [User]()
    @Published var users = [User]()
    private let service = FeedService()
    private let userService = UserService()
    
    // 拆分独立状态
    @Published var followingState: State = .loading
    @Published var followerState: State = .loading

    init(user: User) {
        self.user = user
    }
    
    enum State {
        case loading
        case loaded
        case empty
    }
    
    func fetchFollowingList() {
        guard let uid = user.id else { return }
        followingState = .loading // 加载前重置状态
        
        service.fetchFollowingList(forUid: uid) { users in
            self.followingUsers = users.filter({ $0.id != uid })
            
            if self.followingUsers.count > 0 {
                self.followingState = .loaded
            } else {
                self.fetchUsers(completion: {
                    self.followingState = .empty
                })
            }
        }
    }
    
    func fetchFollowerList() {
        guard let uid = user.id else { return }
        followerState = .loading // 加载前重置状态
        
        service.fetchFollowerList(forUid: uid) { users in
            self.followerUsers = users
            
            if self.followerUsers.count > 0 {
                self.followerState = .loaded
            } else {
                self.fetchUsers(completion: {
                    self.followerState = .empty
                })
            }
        }
    }
    
    // 添加完成回调,确保状态仅更新当前标签的对应值
    func fetchUsers(completion: @escaping () -> Void) {
        userService.fetchUsers { users in
            self.users = users
            completion()
        }
    }
}

2. 修复FollowingView:绑定独立状态+修正Equatable

import SwiftUI

struct FollowingView: View, Equatable {
    @ObservedObject var viewModel: FollowListViewModel
    @State var didRefresh = false
    @Binding var followTab: Int

    static func == (lhs: FollowingView, rhs: FollowingView) -> Bool {
        // 比较所有影响视图显示的关键属性,确保状态变化时视图刷新
        lhs.viewModel.followingState == rhs.viewModel.followingState &&
        lhs.viewModel.followingUsers == rhs.viewModel.followingUsers &&
        lhs.viewModel.users == rhs.viewModel.users &&
        lhs.didRefresh == rhs.didRefresh
    }
    
    var body: some View {
        VStack {
            switch viewModel.followingState {
            case .loading:
                Spacer()
                ProgressView()
                Spacer()
            case .loaded:
                ScrollView (.vertical, showsIndicators: false) {
                    PullToRefresh(coordinateSpaceName: "pullToRefreshFollowing") {
                        viewModel.fetchFollowingList()
                    }
                    LazyVStack {
                        ForEach(viewModel.followingUsers) { user in
                            NavigationLink(
                                destination: LazyView(ProfileView(user: user)),
                                label: { 
                                    ListItemUser(user: user).buttonHidden()
                                        .padding(.top, 12)
                                }
                            )
                        }
                    }
                }.coordinateSpace(name: "pullToRefreshFollowing")
                
            case .empty:
                FollowEmptyStateView(viewModel: viewModel, followTab: $followTab)
            }
        }
        .onAppear {
            viewModel.fetchFollowingList()
        }
    }
}

3. 修复FollowerView:绑定独立状态+修正Equatable

import SwiftUI

struct FollowerView: View, Equatable {
    @ObservedObject var viewModel: FollowListViewModel
    @State var didRefresh = false
    @Binding var followTab: Int

    static func == (lhs: FollowerView, rhs: FollowerView) -> Bool {
        // 比较所有影响视图显示的关键属性,确保状态变化时视图刷新
        lhs.viewModel.followerState == rhs.viewModel.followerState &&
        lhs.viewModel.followerUsers == rhs.viewModel.followerUsers &&
        lhs.viewModel.users == rhs.viewModel.users &&
        lhs.didRefresh == rhs.didRefresh
    }
    
    var body: some View {
        VStack {
            switch viewModel.followerState {
            case .loading:
                Spacer()
                ProgressView()
                Spacer()
            case .loaded:
                ScrollView (.vertical, showsIndicators: false) {
                    PullToRefresh(coordinateSpaceName: "pullToRefreshFollower") {
                        viewModel.fetchFollowerList()
                    }
                    LazyVStack {
                        ForEach(viewModel.followerUsers) { user in
                            NavigationLink(
                                destination: LazyView(ProfileView(user: user)),
                                label: { 
                                    ListItemUser(user: user).buttonHidden()
                                        .padding(.top, 12)
                                }
                            )
                        }
                    }
                }.coordinateSpace(name: "pullToRefreshFollower")
                
            case .empty:
                FollowEmptyStateView(viewModel: viewModel, followTab: $followTab)
            }
        }
        .onAppear {
            viewModel.fetchFollowerList()
        }
    }
}

关键修复点说明

  1. 状态隔离:拆分followingState和followerState,让两个标签的状态完全独立,不会互相覆盖。
  2. 视图刷新逻辑修正:更新Equatable实现,包含所有影响视图显示的属性(状态、用户列表等),确保状态变化时SwiftUI能正确触发视图刷新。
  3. 加载状态重置:每次发起请求前重置对应标签的状态,避免旧状态干扰新请求的显示。
  4. CoordinateSpace冲突解决:给两个视图的下拉刷新设置不同的坐标空间名称,避免功能异常。

内容的提问来源于stack exchange,提问作者amelia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 08:41:57