Xcode报错:Static method ‘buildExpression’ requires that ‘ToolbarItem<(), Button>>>’ conform to ‘View’
Xcode报错:Static method ‘buildExpression’ requires that ‘ToolbarItem<(), Button>>>’ conform to ‘View’

Xcode报错:Static method ‘buildExpression’ requires that ‘ToolbarItem<(), Button>>>’ conform to ‘View’

问题描述

Xcode运行时,toolbar报错:

Static method 'buildExpression' requires that 'ToolbarItem<(), Button<Label<Text, Image>>>' conform to 'View'

报错代码

.toolbar {
    ToolbarItem(placement: .topBarTrailing) {
        Button("Scan", systemImage: "qrcode.viewfinder") {
            isShowingScanner = true
        }
    }
    ToolbarItem(placement: .topBarLeading) {
        EditButton()
    }
    ToolbarItem(placement: .topBarTrailing) {
        Menu("Title") {
            Button("姓名排序", action: {
                _prospects = Query(sort: \Prospect.self)
            })
            Button("最近排序", action: {
                _prospects = Query(sort: \Prospect.self,order: .reverse)
            })
        }
    }
    if selectedProspects.isEmpty == false {
        ToolbarItem(placement: .bottomBar) {
            Button("Delete Selected", action: delete)
        }
    }
}

问题原因

SwiftUI 的 ToolbarItem 的 placement 和 content 没有正确配对。特定情况下,ToolbarItem 需要接受一个单独的 View 类型,而不能直接嵌套复杂的 Menu 和 Button 等组合内容。

下面的Button格式不被ToolbarItem所接受

Button("姓名排序", action: {
    _prospects = Query(sort: \Prospect.self)
})
Button("最近排序", action: {
    _prospects = Query(sort: \Prospect.self,order: .reverse)
})

问题解析

ToolbarItem 的 content 参数要求返回一个符合 View 协议的单一视图。

在 ToolbarItem 中直接使用了复杂的 Menu 组合内容,而 Menu 不直接符合这种上下文要求,SwiftUI 无法推断内容类型。

SwiftUI 的 DSL(领域特定语言)需要明确的类型约束。

解决方案

将 Menu 中的Button改为Button( ){ } 格式:

ToolbarItem(placement: .topBarTrailing) {
    Menu("Title") {
        Button("姓名排序") {
            nameSore = true
        }
        Button("最近排序") {
            nameSore = false
        }
    }
}

完整代码

import SwiftUI
import SwiftData
import CodeScanner


struct ProspectsView: View {
    @Query var prospects: [Prospect]
    @Environment(\.modelContext) var modelContext
    @State private var isShowingScanner = false
    @State private var selectedProspects = Set<Prospect>()
    @State private var hideTips = false
    @State private var nameSore = false
    let filter: FilterType
    
    init(filter: FilterType) {
        self.filter = filter
        
        if filter != .none {
            let showContactedOnly = filter == .contacted
            
            _prospects = Query(filter: #Predicate {
                $0.isContacted == showContactedOnly
            }, sort: [SortDescriptor(\Prospect.name)])
        }
    }
    
    func delete() {
        for prospect in selectedProspects {
            modelContext.delete(prospect)
        }
        selectedProspects=[]
    }
    

    func handleScan(result: Result<ScanResult, ScanError>) {
        isShowingScanner = false
        switch result {
        case .success(let result):
            let details = result.string.components(separatedBy: "\n")
            guard details.count == 2 else { return }
            
            let person = Prospect(name: details[0], emailAddress: details[1], isContacted: false)
            
            modelContext.insert(person)
        case .failure(let error):
            print("Scanning failed: \(error.localizedDescription)")
        }
    }
    
    
    
    var title: String {
        switch filter {
        case .none:
            "Everyone"
        case .contacted:
            "Contacted people"
        case .uncontacted:
            "Uncontacted people"
        }
    }
    
    var body: some View {
        NavigationStack {
            if filter == .none,hideTips == false {
                VStack {
                    HStack {
                        Spacer()
                        Image(systemName: "minus.square.fill")
                            .onTapGesture {
                                hideTips = true
                            }
                    }
                    Image(systemName: "lasso.badge.sparkles")
                        .font(.title)
                    Spacer().frame(height: 10)
                    Text("是否联系了潜在客户")
                }
                .foregroundColor(Color.white)
                .frame(width: 200,height: 100)
                .background(Color.blue)
                .cornerRadius(10)
            }
            UserView(nameSort:nameSore,selectedProspects:$selectedProspects)
            .navigationTitle(title)
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Scan", systemImage: "qrcode.viewfinder") {
                        isShowingScanner = true
                    }
                }
                ToolbarItem(placement: .topBarLeading) {
                    EditButton()
                }
                ToolbarItem(placement: .topBarTrailing) {
                    Menu("排序") {
                        Button("姓名排序") {
                            nameSore = true
                        }
                        Button("最近排序") {
                            nameSore = false
                        }
                    }
                }
                if selectedProspects.isEmpty == false {
                    ToolbarItem(placement: .bottomBar) {
                        Button("Delete Selected", action: delete)
                    }
                }
            }
        }
        .sheet(isPresented: $isShowingScanner) {
            CodeScannerView(codeTypes: [.qr], simulatedData: "Paul Hudson\npaul@hackingwithswift.com", completion: handleScan)
        }
    }
}
#Preview {
    ProspectsView(filter: .none)
        .modelContainer(for: Prospect.self)
}

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注