SwiftUI视图AnyView
SwiftUI视图AnyView

SwiftUI视图AnyView

AnyView 是 SwiftUI 提供的一个类型擦除容器(Type Eraser),用于包装不同类型的视图(View),使它们变成同一种可以统一处理的具体类型。

在SwiftUI中,所有视图(比如Text、Image、VSStack或者自定义的视图)都遵循View协议,但是View是一个带有关联类型的协议(associatedtype Body)。

Swift中带有关联类型的协议,不能直接当作普通变量使用。

例如:

let view: View = Text("Hello") // Use of protocol 'View' as a type must be written 'any View'; this will be an error in a future Swift language mode

因为View是一个类型,所以不能作为具体类型存在,否则会提示协议View作为类型必须写为any View,在未来Swift语言模式中将会出现报错。

AnyView的作用

AnyView可以解决这个问题,它是一个可以封装任何View的容器,只需要把视图封装进AnyView,就可以像处理具体类型一样处理它。

import SwiftUI

struct ContentView: View {
    func makeConditionalView(condition: Bool) -> AnyView {
        if condition {
            return AnyView(Text("Hello"))
        } else {
            return AnyView(Image(systemName: "star"))
        }
    }
    var body: some View {
        VStack {
            makeConditionalView(condition: false)
        }
        .padding()
    }
}

如果不使用AnyView,这个函数就没有办法写出来,因为Text和Image是两个不同的类型,但函数要返回同一种类型。

可以将AnyView类比为,不能把Int和String存在 [Any<T>] 的数组中,因为它们不是同类型。

也不能把Text和Image 存在 [some View] 中,它们也不是同一个具体类型。

就需要使用AnyView将它们包装起来,变成一个统一类型:

let views: [AnyView] = [
    AnyView(Text("Hi")),
    AnyView(Image(systemName: "star")),
    AnyView(VStack { Text("Nested") })
]

使用场景

1、配合AppKit设置视图类型:

class ToolbarWindow: NSWindow {
    init(rectOrigin rect: CGPoint,rectSize size: CGSize, ViewBuilder: () -> any View) {
        // 先创建内容视图
        let swiftUIView = ViewBuilder()
        let hostingControllerView = NSHostingController(rootView: swiftUIView)  // 报错,提示:Type 'any View' cannot conform to 'View'
    }
}

使用NSHostingController将SwiftUI视图转换为AppKit视图时,因为any View是一个协议,所以不能传入NSHostingController。

必须将不确定类型的any View类型强制转换为AnyView,便于NSHostingController使用:

class ToolbarWindow: NSWindow {
    init(rectOrigin rect: CGPoint,rectSize size: CGSize, ViewBuilder: () -> any View) {
        // 先创建内容视图
        let swiftUIView = ViewBuilder()
        let hostingControllerView = NSHostingController(rootView: AnyView(swiftUIView)) // 使用 AnyView 封装遵循 any View的视图
    }
}

总结

AnyView 是一个用来“类型擦除”的容器,让可以把各种不同的 View 类型统一包装为同一个具体类型,将视图作为参数传入,用于参数传递、数组存储、条件分支等场景。

优点在于,统一处理不同的视图类型,提供灵活性,但是性能略有开销,因为SwiftUI不能优化视图树。

   

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

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

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