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

如何将TupleView<_VariadicView.Children>转为自定义视图并访问其内部属性?

问题

我接收的多个视图为TupleView<_VariadicView.Children>类型,尝试转为[AnyView]数组后,无法将AnyView转回原自定义Routable视图,也就没法访问其activated等内部属性。请问这种转回操作是否可行?或者有没有不用TupleView的更好实现方案?

代码示例

视图结构定义

struct View1: View {
    var body: some View {
        VStack {
            Text("page!")
        }
    }
}

struct Routable<Content: View> : View {
    var activated: Bool = false
    let _content: Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self._content = content()
    }
    
    var body: some View {
        _content
    }
}

struct SplashViews: View {
    var body: some View {
        ZStack {
            HStack {
                RouterStack {
                    Routable {
                        View1()
                    }
                    Routable {
                        View1()
                    }
                }
            }
        }
    }
}

转换数组及尝试转回的代码

public typealias Views = _VariadicView.Children

extension TupleView {
    var views: [AnyView] {
        make_array(from: value)
    }
    
    private struct GenericView {
        let body: Any
        
        var any_view: AnyView? {
            AnyView(_fromValue: body)
        }
    }
    
    private func make_array<Tuple>(from tuple: Tuple) -> [AnyView] {
        func convert(child: Mirror.Child) -> AnyView? {
            withUnsafeBytes(of: child.value) { pointer -> AnyView? in
                if case Optional<Any>.some(_) = child.value {
                    print("[TurpleView Children Value]: \(child)")
                    
                    let binded = pointer.bindMemory(to: GenericView.self)
                    return binded.first?.any_view
                } 
                
                return nil
            }
        }
        
        let mirror = Mirror(reflecting: tuple)
        return mirror.children.compactMap(convert)
    }
}

struct RouterStack: View {
    @ObservedObject var _router: Router

    private let _content: [AnyView]
    
    init<Views>(@ViewBuilder content: @escaping () -> TupleView<Views>) {
        self._content = content().views
        self._router = Router(self._content)

        // 尝试转回Routable失败,报错如下:
        // Cannot convert value of type 'AnyView' to type 'Routable<Content>' in coercion
        // Cannot convert value of type 'Routable<Content>' to specified type 'AnyView'
        // Generic parameter 'Content' could not be inferred in cast to 'Routable'
        let x: AnyView = self._content[0] as Routable
        // print("\(x.activated)")
    }
    
    var body: some View {
        NavigationView {
             
        }
    }
}

解决方案

1. 为什么AnyView转回Routable不可行?

AnyView是类型擦除容器,它仅保留了View协议的实现,完全丢失了原视图的具体类型信息。因此无法直接将AnyView强制转回Routable<Content>:编译器既无法推断泛型参数Content的具体类型,也无法验证容器内的视图确实是Routable实例。

2. 无需转换AnyView,直接处理TupleView的泛型内容

TupleView的泛型Views本质是_VariadicView.Children,可以直接遍历它的子视图并访问Routable属性,不需要转成AnyView。核心思路是用协议统一Routable的属性访问:

// 定义协议,统一Routable的属性访问
protocol RoutableProtocol {
    var activated: Bool { get set }
}

extension Routable: RoutableProtocol {}

struct RouterStack: View {
    @ObservedObject var _router: Router
    private let _content: _VariadicView.Children
    
    init(@ViewBuilder content: @escaping () -> TupleView<_VariadicView.Children>) {
        self._content = content().value
        self._router = Router(with: self._content)
        
        // 直接遍历子视图访问属性
        for child in _content {
            if let routableView = child as? any RoutableProtocol {
                print("activated status: \(routableView.activated)")
            }
        }
    }
    
    var body: some View {
        NavigationView {
            ForEach(_content.indices, id: \.self) { index in
                _content[index]
            }
        }
    }
}

3. 更简洁的方案:直接接收[RoutableProtocol]数组

如果不需要依赖ViewBuilder的多视图语法糖,可以让RouterStack直接接收符合协议的数组,彻底避开TupleView:

struct RouterStack: View {
    @ObservedObject var _router: Router
    private let _content: [any RoutableProtocol]
    
    init(content: [any RoutableProtocol]) {
        self._content = content
        self._router = Router(with: content)
        
        // 直接访问属性
        _content.forEach { print($0.activated) }
    }
    
    var body: some View {
        NavigationView {
            ForEach(_content.indices, id: \.self) { index in
                // 将协议类型转回View
                (_content[index] as! some View)
            }
        }
    }
}

// 使用方式
struct SplashViews: View {
    var body: some View {
        ZStack {
            HStack {
                RouterStack(content: [
                    Routable { View1() },
                    Routable { View1() }
                ])
            }
        }
    }
}

4. 保留ViewBuilder语法的优化写法

如果想继续用ViewBuilder的多视图写法,可以重载init方法,直接将_VariadicView.Children转为协议数组:

struct RouterStack: View {
    @ObservedObject var _router: Router
    private let _content: [any RoutableProtocol]
    
    init(@ViewBuilder content: @escaping () -> TupleView<_VariadicView.Children>) {
        let children = content().value
        self._content = children.compactMap { $0 as? any RoutableProtocol }
        self._router = Router(with: self._content)
    }
    
    var body: some View {
        NavigationView {
            ForEach(_content.indices, id: \.self) { index in
                (_content[index] as! some View)
            }
        }
    }
}

// 继续使用原写法
// RouterStack {
//     Routable { View1() }
//     Routable { View1() }
// }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:07:32