Swift UI:当视图中显示空列表时,如何显示错误消息?

Posted

技术标签:

【中文标题】Swift UI:当视图中显示空列表时,如何显示错误消息?【英文标题】:Swift UI: How do I display an error message when an empty List is displayed in a View? 【发布时间】:2021-04-25 20:06:35 【问题描述】:

我有一组Terms,按字母顺序分组。下面的 Swift UI 代码将集合显示为 List,按组划分为多个部分。每个组都根据(可能为空的)搜索字符串进行过滤。因此显示的列表包含Terms 的整个集合、基于搜索字符串的子集,或者不包含任何内容(如果搜索失败):

    var body: some View 
        List() 
                        
            ForEach(terms.groups)  group in
                let groupTerms = filteredTerms(for: group)
                
                if searchText.isEmpty || !groupTerms.isEmpty 
                    Section(header: Text(group.name)) 
                        ForEach(groupTerms)  term in
                            NavigationLink(destination: DetailView(term: term)) 
                                TermsRow(term: term)
                            
                        
                    
                
            
        
    

问题是:我如何显示搜索失败的消息,而不是只显示一个空屏幕?可以确定,只有在外部 ForEach 循环完成且所有 groupTerms 为空时,搜索才会失败,但我无法捕获该信息——如果我贴上@,Swift 不喜欢它ForEach 循环中的 987654327@ 布尔值设置为 true,如果任何 groupTerms 非空,如下所示:

    var searchSucceeded = false     
    var body: some View 
        List() 
            
            ForEach(terms.groups)  group in
                let groupTerms = filteredTerms(for: group)
                
                if searchText.isEmpty || !groupTerms.isEmpty 
                    Section(header: Text(group.name)) 
                        ForEach(groupTerms)  term in
                            NavigationLink(destination: DetailView(term: term)) 
                                TermsRow(term: term)
                            
                        
                    
                  else if !searchText.isEmpty && !groupIdioms.isEmpty 
                    searchSucceeded = true           
                
            
            // Display message here if searchSucceeded is false
        
    

此代码不会在分配给searchSucceeded 的情况下编译。我是 Swift UI 新手。最初的代码是由另一个不再可用的开发人员编写的。我希望有人可以在这里帮助我。

【问题讨论】:

SwiftUI 是一个 UI 框架。它不应该做任何工作searchSucceeded = true 算作工作。let groupTerms = filteredTerms(for: group) 算作工作。将所有“工作”代码移出 SwiftUI 视图并将其放入 class ViewModelstruct 是不可变的 var searchSucceeded = false 这永远不会按原样变异。在您的ViewModel 中将其设为@Published var 【参考方案1】:

与其尝试在ForEach 中设置searchSucceeded,不如创建一个名为displayError 的计算属性来确定是否应显示错误。计算的属性将在每次渲染视图时重新计算,这会受到新属性、@State 更改等的影响。

var displayError : Bool 
    //condition here -- map? reduce?
    return true


var body: some View 
    List() 
        ForEach(terms.groups)  group in
            let groupTerms = filteredTerms(for: group)
            if searchText.isEmpty || !groupTerms.isEmpty 
                Section(header: Text(group.name)) 
                    ForEach(groupTerms)  term in
                        NavigationLink(destination: DetailView(term: term)) 
                            TermsRow(term: term)
                        
                    
                
            
        
        
        if displayError 
            Text("error message")
        
    

你没有包含你的模型代码,所以很难说在那个计算属性中放了什么,但是从你的循环来看,它很可能是一个reduce 或者迭代你的组并查看的东西如果有有效数据。

就像 cmets 中提到的 @lorem 一样,您可以将这种东西移动到视图模型中,但这并不是绝对必要的。如果您有大量数据并且出于资源原因想要限制通过数据的次数,这可能会变得更加重要。

【讨论】:

【参考方案2】:

对于这样的情况 - 您必须绘制完全不同的“This”或“That”,我使用“EitherView”和相应的模型“Either”:

struct EitherView<Right: ComponentView, Left: ComponentView>: ComponentView 
    var state: Either<Right.State, Left.State>

    init(_ state: Either<Right.State, Left.State>) 
        self.state = state
    

    var body: some View 
        switch state 
        case .right(let right):
            Right(right)
        case .left(let left):
            Left(left)
        
    

public enum Either<Right, Left> 
    case right(Right)
    case left(Left)

然后您可以添加模态呈现的视图和加载指示器,如下面的示例所示,旨在实现一个可重用的通用视图来执行所有这些操作:

struct MyView<ContentView: ComponentView>: View 
    typealias Content = ContentView.State
    typealias Modal = ModalState<AlertState, SheetState, ActionSheetState, LoadingState>
    private var state: ViewState<Content, Modal>

    init(_ state: ViewState<Content, Modal>) 
        self.state = state
    

    var body: some View 
        return GeometryReader  geometry in
        ContentView(state.body)
        .frame(width: geometry.size.width,
               height: geometry.size.height,
               alignment: .center)
        
        .alert(item: showAlert)  alert in
            SwiftUI.Alert(title: Text(alert.title),
                          message: Text(alert.message))
        
        .sheet(item: showSheet)  sheet in
            ...
        
        .actionSheet(item: showActionSheet)  actionSheet in
            SwiftUI.ActionSheet(title: Text(actionSheet.title))
        
    

private extension MyView 
    var showAlert: Binding<AlertState?> 
        Binding<AlertState?>(
            get:  self.state.alert ,
            set:  _ in self.state.dismiss() 
        )
    
  ... 

请注意,此示例中的视图状态是不可变的。

为了将这个应用到您的问题中,您只需要编写相应的 modelviewState,最好在视图之外,例如在 viewModel 中。

现在你已经把你的大问题分成小块,每个小块都很容易解决。

【讨论】:

以上是关于Swift UI:当视图中显示空列表时,如何显示错误消息?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift UI 中将嵌套的 JSON 值显示到选取器视图中?

Swift 中的 Xcode:导航栏未显示在 UI 集合视图(模拟器)中

当用户选择文本字段时,如何显示选择器

如果变量不是 nil,如何在 swift UI 中显示文本视图?

我怎样才能使它只有一个空状态或 ui-sortable 显示的放置目标占位符?

Swift UI集合视图 - 如何在Cell中显示“segued”用户输入?