SwiftUI Tips: AnyView 的使用场景

3,464 阅读2分钟

SwiftiUI 提供了一个结构体 AnyView来表示任意一个 View 实例,和 Any 一样可以用来抹除具体的类型。 假设我们有一个页面展示用户的信息,如果没有用户没有登录我们就展示一个登录按钮。根据状态不同,一个 View 可能会返回不同实例类型的 View:

struct UserInfoView: View {
    
    @State var isLogin = false
    
    var body: some View {
        if isLogin {
            return Text("Logined”)
        } else {
            return Button(action: {
                // 去登录
            }, label: {
                return Text("Login")
            })
        }
    }
}

那么上面的代码能不能被编译通过呢? 直觉上我们认为返回的只要是实现了 View 协议的都能满足,应该能编译通过。不过实际上这样写会编译器会提供一个错误:

Function declares an opaque return type, but the return statements in its body do not have matching underlying types

问题就出在前面的关键字 some 上。加了 some 后协议透明,编译器在编译时推断具体代码的实际返回类型,因此要求必须只能有一个确定的类型。具体关于 some 我在之前的博客 Swift 5.1 新特性:透明类型关键字 some 有介绍过。 上面的例子中用户已登录时返回 Text,没登录返回 Button 类型。不是同一种类型,编译器因此抛出错误。为了解决这个问题我们很自然想到用一个通用的类型把所有的 View 都包起来,这样编译器可以编译通过。反正运行的时候是 View 就可以了。这就是 AnyView 的使用场景了,抹掉具体 View 类型,对外有一个统一的类型,上面的代码这样改一下就可以了:

    var body: some View {
        if isLogin {
            return AnyView(
                Text("User logined")
            )
        } else {
            return AnyView(
                Button(action: {
                    // 去登录
                }, label: {
                    return Text("Login")
                })
            )
        }
    }