teissler, to SwiftUI
@teissler@hachyderm.io avatar

WWDC24 is right around the corner. Before the excitement and rush of what's to come, I want to share some navigation fun facts and tips. None of this is new, having been discussed previously in sessions, etc. From talking with developers, these are parts of the system that are often overlooked, or forgotten about This is all running on macOS Sonoma 14.4, Xcode 15.3

teissler,
@teissler@hachyderm.io avatar
  1. List selection can drive navigation pushes. You can populate the selection binding with a value-destination NavigationLink, or, any other way you would normally populate it.

A video showing a compact NavigationSplitView on iPhone Previews next to a code sample. The preview shows a list of with 4 elements, 3 navigation links, and 1 Text row. All 4 rows get a grey highlight when active. The non-navigation link row, the simple tagged Text, has no chevron, but still highlights gray in sync with the push progress. Code: import SwiftUI enum Sports: String, Hashable { case pickleball case dance case archery case volleyball } struct Day1: View { @State private var selection: Sports? var body: some View { NavigationSplitView { VStack { List(selection: $selection) { NavigationLink("Pickleball", value: Sports.pickleball) NavigationLink("Dance", value: Sports.dance) NavigationLink("Archery", value: Sports.archery) Text("Volleyball").tag(Sports.volleyball) } } } detail: { if let selection { VStack { Text(selection.rawValue.capitalized(with: .current)) Image(systemName: "figure.(selection.rawValue)") } } else { ContentUnavailableView( "Make a selection", systemImage: "volleyball") } } } } { Day1() }

teissler,
@teissler@hachyderm.io avatar
  1. The navigation APIs give different options for pushing a view or root replacing a column. In trivial examples, the API used seems arbitrary, but in larger apps, each version has tradeoffs. I could spell the example from yesterday like this instead:

A sample of SwiftUI code running macOS previews. The preview picks Feature 1 in the sidebar, changing the view shown in the detail. Then it pushes the red view with the “Push Red” button in the detail. Then, Feature 2 is selected in the sidebar, popping the path back to empty and displaying the original detail view for Feature 2. Green is pushed, then Feature 2 is command clicked, getting us back to the starting state. import SwiftUI struct Day3: View { @State var path = NavigationPath() @State var selection: Int? var body: some View { NavigationSplitView { List(selection: $selection) { NavigationLink("Feature 1", value: 1) NavigationLink("Feature 2", value: 2) } } detail: { NavigationStack(path: $path) { if let selection { feature(for: selection) } else { Text("Select a feature") } } } } @ViewBuilder func feature(for selection: Int) -> some View { switch selection { case 1: Feature1() case 2: Feature2() default: fatalError() } } } { Day3() }

teissler,
@teissler@hachyderm.io avatar
  1. Visiting first principles of NavigationStack for the next few tips. NavigationStack takes your app's data model, and transforms it into a stack of views. NavigationStack also supports fire-and-forget style navigation — these are standalone navigation actions that don't modify the client's state, i.e. the navigation path. The navigation system tracks that state internally for you. These are the simplest, so let's look at them first.

A video of a NavigationStack on watchOS. The stack pushes a view-destination navigation link. There is an onChange modifier for the navigation path, but it never prints anything because the client’s view of the path doesn’t change. Code: import SwiftUI struct Day6: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { NavigationLink( destination: { RainbowSymbol() }, label: { Label("Navigate", systemImage: "compass.drawing") .font(.headline) }) }.onChange(of: path) { oldValue, newValue in print("path changed from (oldValue) to (newValue)") } } } private struct RainbowSymbol: View { var body: some View { Image( systemName: "cloud.rainbow.half.fill") .symbolRenderingMode(.multicolor) .symbolEffect( .variableColor.hideInactiveLayers, options: .repeating) .font(.system(size: 90)) } } { Day6() }

teissler,
@teissler@hachyderm.io avatar
  1. NavigationLink(_ title:value:) is a "value-destination" link. Conceptually, it navigates to a value. From talking with developers, it's a misconception that this must be paired with a .navigationDestination(for:…) modifier, mapping directly to a view. As we saw in Day 3, it can drive List selection, mapping indirectly to a view.
    This link often uses data-driven navigation (the opposite of fire-and-forget) — causing changes in client state like appending to a path seen here.

Xcode Previews showing a code sample and an iPhone. When the link in the iPhone is tapped, the stack pushes a pink view. Simultaneously, the console prints “New Path: [7]”. After popping the view back off, the console prints “New Path: []” Code: import SwiftUI struct Day7: View {

teissler,
@teissler@hachyderm.io avatar

11 cont) Let's examine a navigation cycle. This example shows a navigation cycle that is a client bug. It's hard to spot why immediately. The Day11_Bad_Cycle view has an environmental dependency on MyEnvironmentKey. The value of MyEnvironmentKey is a closure. Closures are not equatable or comparable in Swift, the graph always has to assume they have changed value. Down below, the navigationDestination captures self by means of localState! When I tap push, the app freezes up 😮 #SwiftUI

A video of Xcode previews. The phone has a single button “Push”. When tapped, the “Push” gets a pressed appearance, and the app freezes, never pushing. import SwiftUI struct Day11_Bad_Cycle: View { private var localState: String = "loop" @Environment(.myCustomValue) var functional var body: some View { NavigationLink("Push", value: 10) .navigationDestination(for: Int.self) { _ in OtherView(string: localState) // Captures

teissler,
@teissler@hachyderm.io avatar

11 final) In this version, I've wrapped the closure up in a Equatable Box. For a proof-of-concept, I use the @Namespace property wrapper to establish identity, given that I know that closure values identity is tied to the Namespace.The cycle is solved. The takeaway, make your environment values Equatable when possible, and when you can't, don't capture them as part of .navigationDestinations #SwiftUI

Shout out to a particular follower who knows where they are who filed quality FBs about this.

The same example as before, but conforming MyEnvironmentKey.Value to Equatable stops the cycle. The view pushes and pops successfully import SwiftUI struct Day11: View {

teissler,
@teissler@hachyderm.io avatar

12 final) Given the size classes and interactions supported, fixing the sidebar is a rare, and currently unsupported (FBs welcome). On iPad in certain size classes users can drag from the leading edge to reveal the sidebar. Lest we forget the iPad mini in portrait where a fixed sidebar would feel cramped. And lest we really not forget this peek interaction on macOS #SwiftUI #21DaysOfSwiftUINavigation

An Xcode Previews window showing a macOS preview of a NavigationSplitView. Hovering the mouse some 5-10 points from the leading edge of the window reveal a peek of the sidebar. The user can grab the peeking sidebar and drag it open from a collapsed state. import SwiftUI struct Day12: View { var body: some View { NavigationSplitView { List { ForEach(0...10, id: .self) { i in NavigationLink("Option (i)", value: i) } } } detail:{ ContentUnavailableView( "Make your selection", systemImage: "circle.hexagongrid.fill") .symbolRenderingMode(.multicolor) .symbolEffect(.pulse, options: .repeating) } } } #Preview { Day12() }

teissler,
@teissler@hachyderm.io avatar
  1. macOS supports Cmd-click deselect out of the box. This works with value-destination and view destination links. You'll notice the view-destination link’s (Settings) selection is cleared when the environmental action dismiss is called from the detail view #SwiftUI #21DaysOfSwiftUINavigation

macOS Xcode Previews showing a basic NavigationSplitView. The 3 options in the sidebar as successively clicked and Command-clicked, which dismisses the presented view. Additionally, a button that calls the dismiss action form the detail column will also dismiss the presented view and clear selection. import SwiftUI struct Day13: View { var body: some View { NavigationSplitView { List { NavigationLink(value: "House") { Label("Home", systemImage: "house") } NavigationLink(value: "Profile") { Label("Profile", systemImage: "person") } NavigationLink { DismissibleView() } label: { Label( "Settings", systemImage: "gear") } } .navigationDestination(for: String.self) { str in if str == "House" { Color.accentColor } else { Color.mint } } } detail: { Text("No selection") } } } struct DismissibleView: View { @Environment(.dismiss) var dismiss var body: some View { ZStack { Color.gray Button("Dismiss") { dismiss() } } } } #Preview { Day13() }

boilingsteam, to linux
@boilingsteam@mastodon.cloud avatar
nautilebleu, to sketch French
@nautilebleu@mamot.fr avatar
kubikpixel, to twitter
@kubikpixel@chaos.social avatar

deleted_by_author

  • Loading...
  • cosmic040,
    @cosmic040@chaos.social avatar
    box464, to mastodon
    @box464@mastodon.social avatar

    I see some conversations again about how Mastodon Link Previews work. Here’s my research on that topic from last year.

    https://box464.com/posts/mastodon-preview-cards/

    LeonardoDiOttio,

    @box464 I wish the bug with and for preview images would finally be fixed.

    Sorting issue 27370 would be a good start toward a proper fix: https://github.com/mastodon/mastodon/issues/27370

    dimsumthinking, to random
    @dimsumthinking@mastodon.social avatar

    WWDC Hopes -

    • SwiftData getting some love. The year Combine was released was the only year we saw sessions on it and that ended up telling us a lot.

    • MusicKit getting some updates that align it better with SwiftUI and Swift async APIs (I don't expect this)

    • AI/ML tools and APIs for us - I don't care so much about LLMs as other applications.

    • More strategic macros provided along the lines of @model, @Observable, and that make coding experience nicer.

    • Swift 6 preview

    jann, to random
    @jann@twit.social avatar

    Listening to @SuperSideshow ! Go to it, guys!

    @Jayslacks @mikebrew

    chronicallydave, to bloomington_in
    @chronicallydave@mastodon.social avatar
    tuxedocomputers, (edited ) to KDE German
    @tuxedocomputers@linuxrocks.online avatar

    The #KDE Plasma 6 MegaRelease has landed!

    Read our article here to find out what's #new and what users can expect. And: We are providing you with an #ISO image with Plasma 6 as a technical #preview.

    @kde

    https://www.tuxedocomputers.com/en/The-KDE-Plasma-6-MegaRelease-has-landed.tuxedo

    #linux #opensource #newrelease #plasma6

    shaknais, to free
    @shaknais@mastodon.social avatar
    SvanDendragon, to art French
    @SvanDendragon@imaginair.es avatar

    You're not ready for what is coming 👀​

    boilingsteam, to linux
    @boilingsteam@mastodon.cloud avatar
    Jeehut, to apple
    @Jeehut@iosdev.space avatar

    Just filed 9 radars to Apple after a long programming session, mostly reporting shortcomings in 1. Hopefully some of these will be addressed in June with ! 🤞

    Time to sleep now – recharging to kick off another new Vision app tomorrow ! 🚀💤

    nighthawk,
    @nighthawk@aus.social avatar

    @Jeehut Great list!

    On the second last, that should work already: #Preview(windowStyle: .volumetric, traits: .fixedLayout(width: 1, height: 1, depth: 1))

    itnewsbot, to gaming

    Steam Next Fest: Eight game demos that stood out from the crowd - Can you tell which of these seemingly identical bits of Steam iconograp... - https://arstechnica.com/?p=2003301

    darylbaxter, to random
    @darylbaxter@mastodon.social avatar

    My brief of is here!
    For now, I'm able to show off the first level from each game in this video.

    All before I give my full impressions on Wednesday. Enjoy.
    https://www.youtube.com/watch?v=2fKMiy9DWto

    shnhrrsn, to SwiftUI

    Is anyone using SwiftUI/Xcode Previews in a full app?

    I ten to use (and love) them for initial prototyping but as soon as I start really building the app, I end up tearing them out.

    Once the codebase gets larger than a handful of views, they're too slow almost become annoying — curious if there ways to scale them up?

    @swiftui

    dandylyons,
    @dandylyons@iosdev.space avatar

    @litso @shnhrrsn @swiftui

    I love this approach and it has helped so much.

    But my Previews are basically dead ever since I started using macros.

    For one, using ‘’ is amazing, but there are certain edge cases where it bugs out.

    Also, any package with macros depend on SwiftSyntax which is massive and so my previews give up before the compiler can finish.

    evi, to random
    StewartLynch, to random
    @StewartLynch@iosdev.space avatar

    I know that we can change the default userLocation for the simulators, but is there a way in SwiftUI to set that for the provider? I am working on a new series for the new API and would rather not run on Simulator to see it. Currently, I get Apple Park as my location. Can it be changed?

  • All
  • Subscribed
  • Moderated
  • Favorites
  • Leos
  • ngwrru68w68
  • InstantRegret
  • thenastyranch
  • magazineikmin
  • GTA5RPClips
  • rosin
  • osvaldo12
  • tacticalgear
  • Youngstown
  • slotface
  • khanakhh
  • kavyap
  • DreamBathrooms
  • JUstTest
  • modclub
  • everett
  • provamag3
  • cubers
  • cisconetworking
  • ethstaker
  • Durango
  • mdbf
  • anitta
  • megavids
  • normalnudes
  • tester
  • lostlight
  • All magazines