liveview-native / liveview-client-swiftui

MIT License
379 stars 39 forks source link

[Bug]: navigationTitle modifier not working when nested inside TabView #1431

Closed hypno2000 closed 1 month ago

hypno2000 commented 2 months ago

What happened?

I can't get the navigationTitle to work when it is nested inside TabView. I am not sure if i am doing something wrong or it is a bug.

This swift code:

TabView {
    NavigationStack {
        List {
            Text("Foo")
            Text("Bar")
            Text("Baz")
        }
        .navigationTitle("Title")
    }
    .tabItem {
        Label("Tab", systemImage: "play.circle")
    }
}

produces this result:

Screenshot 2024-09-02 at 21 30 19

This LVN code:

<TabView>
  <NavigationStack style="tabItem(:label)">
    <Label template="label" systemImage="play.circle">
      <Text>Tab</Text>
    </Label>
    <List style='navigationTitle(attr("title"))' title="Title">
      <Text>Foo</Text>
      <Text>Bar</Text>
      <Text>Baz</Text>
    </List>
  </NavigationStack>
</TabView>

produces this result (no title visible): IMG_9DAD1EDA1A32-1

When TabView container is removed the result is this (title is visible):

IMG_F4E887ABB5A7-1

Library Version

0.3.0

Xcode Version

15.4

Swift Version

5.10

On which device or simulator are you running into the problem?

iPhone

Target Device Operating System Version

17.6.1

Relevant log output

No response

bcardarella commented 2 months ago

@hypno2000 which template are you putting NavigationStack into?

hypno2000 commented 2 months ago

it is quite blank project, generated with phx.new, lvn.setup.config and lvn.setup.gen.

lib/my_app_web/router.ex:

  scope "/", MyAppWeb do
    pipe_through :browser

    live "/", HomeLive
  end

lib/my_app_web/live/home_live.swiftui.ex

defmodule MyAppWeb.HomeLive.SwiftUI do
  use MyAppNative, [:render_component, format: :swiftui]

  def render(assigns, _) do
    ~LVN"""
    <TabView>
      <NavigationStack style="tabItem(:label)">
        <Label template="label" systemImage="play.circle">
          <Text>Tab</Text>
        </Label>
        <List style='navigationTitle(attr("title"))' title="Title">
          <Text>Foo</Text>
          <Text>Bar</Text>
          <Text>Baz</Text>
        </List>
      </NavigationStack>
    </TabView>
    """
  end
end

Now when u asked this i started looking into layouts and found that the whole thing is already wrapped to NavigationStack in lib/my_app_web/components/layouts_swiftui/root.swiftui.neex. I should probably move the TabView to layouts to avoid nested NavigationStack. Will try that now.

bcardarella commented 2 months ago

So we need to write this up but the NavigationStack views I believe are being ignored. It's due to how we have to implement navigation. Those views must be in the root layout to work, you can try adding the TabView/NavigationStack to the root layout then the rest into the app template

bcardarella commented 2 months ago

the NavigationStack views I believe are being ignored.

to be clear, just in the app layout and app templates. Anythign navigation in the root template is used but only provided by the dead render

hypno2000 commented 2 months ago

With my previous approach i had TabView -> NavigationStack but this would not work with putting it to root layout as with multiple tabs each tab would have its own NavigationStack but there is only one <%= @inner_content %>. So i tried having NavigationStack -> TabView instead, so i left the root layout default with one NavigationStack and put this to my template:

<TabView>
  <List style='tabItem(:label); navigationTitle(attr("title"))' title="Title">
    <Label template="label" systemImage="play.circle">
      <Text>Tab</Text>
    </Label>
    <Text>Foo</Text>
    <Text>Bar</Text>
    <Text>Baz</Text>
  </List>
</TabView>

interestingly the result is exactly the same as before. With the above template i get this: Screenshot 2024-09-02 at 23 26 44

Removing TabView the title shows: Screenshot 2024-09-02 at 23 27 01

But it is consistent with how it works with swift:

NavigationStack {
    TabView {
            List {
                Text("Foo")
                Text("Bar")
                Text("Baz")
            }
            .tabItem {
                Label("Tab", systemImage: "play.circle")
            }
            .navigationTitle("Title")
        }
}

Screenshot 2024-09-02 at 23 30 48

So i guess i can't have TabView nested inside NavigationStack, it only works with NavigationStack inside every tab (explained also here https://stackoverflow.com/questions/60924367/swiftui-navigationbaritems-disappear-in-tabview/60933674#60933674) but i have trouble expressing that with LVN right now.

bcardarella commented 2 months ago

I'll have Carson weigh in tomorrow. The mysteries of what SwiftUI likes and doesn't like elude me sometimes

carson-katri commented 2 months ago

There's no good way of putting the NavigationStack inside the TabView at the moment. The best option for that particular structure would be to put the tabs in Swift, and use NavigationStack in your root layout.

TabView {
  #LiveView(.localhost(path: "/home"))
    .tabItem { ... }
  #LiveView(.localhost(path: "/settings"))
    .tabItem { ... }
}

If you're ok putting the TabView inside the NavigationStack, I wrote a Gist explaining one way that could be setup: https://gist.github.com/carson-katri/d9780abf4b15d1d071facf363e2b6124

Or see how it's done in the Lax app: https://github.com/jtormey/lax/blob/4fb54e6071abf69b65a2fdc80db271c3e8fc47c0/lib/lax_web/live/chat_live.swiftui.ex#L167

carson-katri commented 2 months ago

Regarding the navigationTitle in your example: You could set the title attribute based on the selected tab.

bcardarella commented 1 month ago

Closing as something we should address in the future