0

I have code like this

import SwiftUI

struct TabItemModifier<TabItem>: ViewModifier where TabItem: View {

    var tabItem: () -> TabItem

    func body(content: Content) -> some View {
        return TabItemView(tabItem: tabItem, content: content)
    }
}

struct TabItemView<TabItem, Content> : View where Content: View, TabItem : View {

    var tabItem: () -> TabItem
    var content: Content

    var body: some View {
        content
    }
}


extension View {

    func withTabItem<V>(@ViewBuilder _ label: @escaping () -> V) -> some View where V: View{
        ModifiedContent(content: self, modifier: TabItemModifier(tabItem: label))
    }
}

It applies modifier withTabItem that should return some type TabItemView which contains tabItem and content in properties and renders just content.

Then in other place in code I would like to cast View into this TabItemView to access tabItem property from it like below:

if let tabbed = views.0 as? TabItemView {
      tabItems.append(tabbed.tabItem())
 }

But this casting is not possible.

UPDATE

I've changed it this way

 var body: some View {

        MenuView {

            Page1()

            Page2()
                .menuTabItem(tag: 1) {
                   TabItemView(systemImage: "person", title: "Tab 2")
                }

And the new implementation of this ViewModifier is very simple, like this

 func menuTabItem<T>(tag: Int, @ViewBuilder _ tabItem: @escaping () -> T) -> some View
                       where T: View {

        ModifiedContent(content: AnyView(self),
                           modifier: Click5MenuItemModifier(
                                tag: tag,
                                menuItem: nil,
                                tabItem:  AnyView(tabItem())
            )
        )
    } 


struct MenuItemModifier: ViewModifier {

    var tag: Int
    var menuItem: AnyView?
    var tabItem: AnyView?

    func body(content: Content) -> some View {
        return content
    }
}

But the problem is that I can only use it directly inside MenuView. The usage of this modifier inside Page1(), causes that view is hidden under Page1, and then inside implementation of MenuView I cannot access this modifier with above casting, there is also no reference to this modifier chain.

Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143
  • `tabItems.append(tabbed.tabItem)` should hopefully work. – user1046037 Nov 25 '19 at 11:38
  • it doesn't work as I cannot do casting on TabItemView, I can only do on something like this ModifiedContent> but I want to have here instead of Text possibly some View equivalent and just access tabItem as some View not specific view, and view be also some generic view not specific Text – Michał Ziobro Nov 25 '19 at 11:52
  • Please edit the question to declare `tabItems`, give an example that can be fully tested at our end to understand the problem – user1046037 Nov 25 '19 at 11:54
  • tabItems is just [AnyView] array and tabbed.tabItem() should be @ViewBuilder, I will add modified version as I go to replacing generics with AnyView – Michał Ziobro Nov 25 '19 at 12:12
  • 1
    Modifying the view hierarchy this way looks very wrong. SwiftUI Views are value types and are regenerated from scratch all the time. They *describe* the view hierarchy; they aren't the actual on-screen views. You don't reach in and modify the existing data; you compute all the tabs you need, and TabItemView either takes them as a parameter, or reads them from `Environment`. Even if you get this working, I expect it not to update correctly. – Rob Napier Nov 25 '19 at 14:40
  • I am considering how Apple Team is doing this with .tabItem().tag() on Views inside TabView – Michał Ziobro Nov 25 '19 at 15:40
  • Almost certainly using preferences, which allow subviews to bubble information up to the containing views. ("Preferences" is a horrible name for this. It has nothing to do with options or preferences.) SwiftUI Lab has a good introduction, since they're very poorly documented by Apple: https://swiftui-lab.com/communicating-with-the-view-tree-part-1/ – Rob Napier Nov 25 '19 at 15:52
  • See also Objc.io's introduction: http://talk.objc.io/episodes/S01E179-building-a-shopping-cart-cleanup-refactoring – Rob Napier Nov 25 '19 at 15:53
  • If you feel you need a lot of AnyView, then you're also likely on the wrong track. AnyView should be very rare; it breaks major SwiftUI optimizations. It's just an escape hatch for certain kinds of completely dynamic content, i.e. content that cannot be known at compile-time, even as "one of a list of possible views." – Rob Napier Nov 25 '19 at 15:55
  • 1
    Thanks @RobNapier I think I need to read this articles about "Preferences" and this will be way to solve this tabItem views addition as I think. As my current solution works rather bad. – Michał Ziobro Nov 25 '19 at 18:02

1 Answers1

0

TabItemView is a struct has two associated type. You have to specify the concrete type when using it. Something like ...as? TabItemView<Text, Color>. However, it's impossible to specify an opaque type(View). So my suggestion is:

Change TabItemView's content to AnyView and keep tabItem's type be the same as tabItems's type.

ribilynn
  • 460
  • 5
  • 21