liveview-native / liveview-client-swiftui

MIT License
372 stars 35 forks source link

[Bug]: Views in List view in Tab view not updating correctly #1441

Open jtormey opened 1 week ago

jtormey commented 1 week ago

What happened?

I noticed that if an event updates the order of items in a list and at the same time updates a property of a list item in a tab view, after navigating from another tab, then the list no longer behaves as expected after the first update.

Without reordering (working):

https://github.com/user-attachments/assets/a8662731-7a09-4f33-8fce-b170505434c6

With reordering (not working) after navigating from another tab:

https://github.com/user-attachments/assets/5f048a15-ed56-4b08-b483-225605b3a735

Note that the circular indicator does not change even as the list order changes.

Relevant code:

<TabView selection={@selection} phx-change={@on_change}>
  <VStack tag="..." style="tabItem(:item)">
    <Label template="item" systemImage="...">
      ...
    </Label>
    <List style="listStyle(.plain)">
      <Section>
        <Text template="header">
          Meat counter
        </Text>
        <HStack id={"item_" <> item.id} :for={item <- @items}>
          <Image
            systemName={if item.in_cart, do: "largecircle.fill.circle", else: "circle"}
            style="imageScale(.large); foregroundColor(.accentColor);"
            phx-click="toggle_completed"
            phx-value-id={item.id}
          />
          <Text>
            <%= item.title %>
          </Text>
          <LabeledContent>
            <%= item.detail %>
          </LabeledContent>
        </HStack>
      </Section>
    </List>
  </VStack>
</TabView>

Library Version

0.3.0

Xcode Version

15.4 (15F31d)

Swift Version

No response

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

iPhone

Target Device Operating System Version

17.2

Relevant log output

No response

bcardarella commented 5 days ago

@jtormey can you share the event handler for toggle_completed ?

jtormey commented 3 days ago

Handler is as follows:

  def handle_event("toggle_completed", %{"id" => id}, socket) do
    items =
      Enum.map(socket.assigns.items, fn
        %{id: ^id} = item -> %{item | in_cart: not item.in_cart}
        item -> item
      end)
      # BUG: LVN behavior changes after sorting
      |> sort_items()

    {:noreply, assign(socket, :items, items)}
  end

  def sort_items(items) do
    Enum.sort_by(items, & &1.in_cart)
  end
carson-katri commented 3 days ago

I'm having trouble replicating this on main. Does this look correct to replicate it?

I added an animation to visualize the items moving more easily, but the behavior was the same with/without it for me.

https://github.com/user-attachments/assets/d7503d25-bcd4-4e53-afee-b4421379cd7c

This is from an iOS 18 simulator, but I also tried on a iOS 17.5 simulator.

Code ```html Tab 1
Meat counter item.id} :for={item <- @items}> <%= item.title %> <%= item.detail %>
``` ```ex defmodule CoreIntegrationWeb.HomeLive do use CoreIntegrationWeb, :live_view use CoreIntegrationNative, :live_view def mount(_params, _session, socket) do {:ok, socket |> assign(:items, [ %{ id: "0", in_cart: false, title: "Item 1", detail: "Detail" }, %{ id: "1", in_cart: false, title: "Item 2", detail: "Detail" }, %{ id: "2", in_cart: false, title: "Item 3", detail: "Detail" }, %{ id: "3", in_cart: false, title: "Item 4", detail: "Detail" }, %{ id: "4", in_cart: false, title: "Item 5", detail: "Detail" }, %{ id: "5", in_cart: false, title: "Item 6", detail: "Detail" }, ])} end def render(assigns) do ~H""" """ end def handle_event("toggle_completed", %{"id" => id}, socket) do items = Enum.map(socket.assigns.items, fn %{id: ^id} = item -> %{item | in_cart: not item.in_cart} item -> item end) # BUG: LVN behavior changes after sorting |> sort_items() {:noreply, assign(socket, :items, items)} end def sort_items(items) do Enum.sort_by(items, & &1.in_cart) end end ```
jtormey commented 2 days ago

That looks correct, maybe I need to update my package versions.

jtormey commented 1 day ago

@carson-katri I believe I've narrowed in on the issue, I'm only able to recreate this when TabView has a phx-change="swiftui_tab_changed" attribute, which I noticed your example doesn't have.