Okay, so by adding a .navigationDestination(for:) to the sidebar column, it fixes the content to detail column animation.

However, now the sidebar to content column animation is broken. I've tried fiddling around with .navigationBarTitleDisplayMode(.large), etc. Nada.

I need to lay down.

Follow

I refuse to sleep until I have found a solution to this, which is unfortunate, as it means I may never sleep again 🥲

I've now officially arrived in crazy town, and started wrapping UINavigationController myself.

What's interesting is that the SwiftUI environment is still automatically adding nav items to the controller, so I've actually got the same broken animations as with NavigationStack. At least it’s…consistent.

Running out of ideas now 😅

Hallelujah! 🙌

Finally got a workaround for the NavigationSplitView regression in iOS 16.4.

Transitions stop working when the content column no longer contains a List (say you have a “Select Mailbox" view, a Grid, etc).

Here's a gist of the issue: gist.github.com/phillipcaudell

It seems to suddenly start behaving like a NavigationStack, which is why adding a navigationDestination sorta works (except now you've got wonky nav animations, and it still sometimes fails)…

…so instead, I'm switching to a Stack when compact. Only issue is getting NavigationLink, SplitView and Stack to play together.

In the end I found it was easier to remove NavigationLinks all together, and instead use tags. Lists then use this as their selection value.

Then, using those selections, I'm computing a path to drive the Stack. Once I caught EditMode, it all worked.

No jittery toolbars. Smooth transitions. Consistent and predictable across iPhone/iPad/Mac.

Thank god.

If there's any poor souls stumbling across this in the future, here's a gist to my solution: gist.github.com/phillipcaudell

Now to refactor this back into Big Mail 🥲

Okay, another update on my Navigation Saga, because sharing is caring.

After integrating, I noticed selected rows wouldn't unselect when popping back.

Turns out using .tags() will only work if:

1) The root List is bound directly to an ObservedObject. A Binding to the same value in an ObservableObject won't work.

2) The List must only contain Text or Label views (so no pinned Grid at the top).

I'm also not sure why the Binding gets fired so many times VS the ObservedObject either?

Anyways I'm back to use NavigationLinks(value:) again, which this time seems to be behaving.

I think the reason it wasn't working last time is because I had installed a navigationDestination on my Sidebar (which is shared between compact and regular size class). It looks like it was confusing NavigationSplitView.

Now I have the navigationDestination installed just in the compact size class, and things seem okay…

Building a low level IMAP/SMTP library has been a breeze compared to understanding how to use these APIs correctly 😅

And here's the rub with SwiftUI: I'm sure there are certain things I'm still doing “the wrong way”, despite doing this everyday, and reading all the docs and examples I can.

But because there have been so many genuine issues (as seen by others), my default position is “Well, the API must be broken then”. That's not a helpful mindset when debugging.

@phill I’m sure you almost got this! 🤓 Maybe a 5 min break is the push you need for it to make it work. Remember that active breaks (like a walk) always help but we never realise we needed till it’s too late 😉

@cocoataster this is probably the best advice I've received today 😅 Thank you!

@phill You can build your own NavigationView with pure SwiftUI. It works *way* better.

A lot of the bugs in SwiftUI come from buggy wrappers around UIKit. This is why it’s better on WatchOS and even worse on the Mac.

Pure SwiftUI, when not using views that wrap complex UIKit views tend to work much better and be less buggy.

It’s a lot of work, but you’ll get many benefits too. E.g. MatchedGeometryEffecf across pages which doesn’t work with NavigationView/Stack

@phill Now, recreating the UI perfectly for NavigationStack won’t be super easy, but it’s possible to get pretty close.

You need an Array of views and you need to render the last two views at all times.

The edge-swipe means putting a thin invisible view over the whole screen and use drag gestures etc.

I’m doing something similar in a side project, but I’m not even trying to duplicate the UI 1:1.

@nmn it’s certainly possible, but you end up in that “uncanny valley” of UI where it’s close, but it’s not quite right. Also I can’t think of a way around using erasure/AnyView, which has its own set of drawbacks.

@phill “Uncanny Valley” is a fair concern which is why we decided to deviate further from the “default” look.

RE: Erasure/AnyView, works well in this use-case IMO. Since the Screen/Route View is the one getting erased, it doesn’t matter since whenever you change the route you want a fresh render anyway.

@phill so, a TestFlight of big mail 2 soon ? 😉

@phill The API is genuinely broken a lot of the times. Specially the whole navigation stack is hopelessly broken, it better after iOS 16 but not enough.

@phill Ha, yeah that's the """fun""" in SwiftUI ...!

Thanks for sharing this. This is pretty involved!

My personal notes about edge cases, corner cases, and undocumented ("flip"?) cases are growing alarmingly.

I don't even have that many "this is not what you'd expect" observation logs for TextKit!

@phill When can we expect a TestFlight build? Would be great to get the possibility to join.

Sign in to participate in the conversation
The Not So Big Company

The home of The Not So Big Company on Mastodon.