UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Sliding out one view for another

Forums > macOS

I'm a bit stuck. Does anyone know of a SwiftUI macOS tutorial that demonstrates double clicking a list item and having that list view animated to slide out to the left replaced with another view? This is very easy with a NavigationLink in iOS / iPadOS and triggered by a single tap in the list, but I'm not really sure how to do something like this in macOS. Definitely has to be a double click to trigger it on macOS though because a single click is needed to make a selection in the list (to move or delete items in the list).

Can anyone here point me in the right direction?

3      

So, I have some code to add a way to detect a double-click in my list:

import SwiftUI

extension View {
    func onDoubleClick(handler: @escaping() -> Void) -> some View {
        modifier(DoubleClickHandler(handler: handler))
    }
}

struct DoubleClickHandler: ViewModifier {
    let handler: () -> Void
    func body(content: Content) -> some View {
        content.overlay {
            DoubleClickListeningViewRepresentable(handler: handler)
        }
    }
}

struct DoubleClickListeningViewRepresentable: NSViewRepresentable {
    let handler: () -> Void
    func makeNSView(context: Context) -> DoubleClickListeningView {
        DoubleClickListeningView(handler: handler)
    }
    func updateNSView(_ nsView: DoubleClickListeningView, context: Context) {}
}

class DoubleClickListeningView: NSView {
    let handler: () -> Void

    init(handler: @escaping () -> Void) {
        self.handler = handler
        super.init(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        if event.clickCount == 2 {
            handler()
        }
    }
}

that enables me to put something like as a modifier on my list:

                .onDoubleClick {
                    getRecord()
                }

in the function I can get the .ID for the selected line (which is what List(selection: ) is set to) and reverse that back to get to the actual item in my database that matches and grab whatever data I need for my new view.

But what I can't figure out is how to get from there to replacing my list view with my new view (ideally with an animation to make it appear to be sliding the list out of view as my new view slides in so the user has some context of what it is doing).

The new view will eventually be a table of sub-contents of the item double clicked in the first list view and I'll have a toolbar item to go back to the top level list. Since both lists are rather wide with multiple columns, it makes more sense to replace one view with the other rather than have them side-by-side (this is so easy with a navigation link on iOS and iPadOS where a single tap makes sense and it even handles the navigation). The table view will also be opening a sheet when double clicking on items within that (which I know how to do), but it also makes it impractical to have the double click of my list view display a sheet of my table... and then having another sheet on top of that.

3      

A couple of other things to note. Using a NavigationStack and a NavigationLink in a list appears to provide one view replacing the other I would like (and it even automatically provides the back button in the toolbar) but, not the animation. I'm also trying to have support for macOS 12 and NavigationStack isn't available for that. And then there is clicking items in my list with the NavigationLink performs the action on a single click (and I need double click for the action so the user can select, move, and delete items in the primary list without the new view sliding in on them. The latter (getting it to only do its thing on double click) is the bigger issue for me. If I could get it working with a double click and the only way to do so requires macOS 13 or later (using a NavigationStack) then I'd be disappointed a bit about not supporting macOS 12, but at the same time, it is what it is.

3      

I'm starting to think that maybe I need to tackle this in a different way. My project has a sidebar which then presents the content view when an item in the sidebar list is clicked with a NavigationLink. The list that appears as the content is an overview (of sorts). On double clicking an item in the overview list, the sub view should slide in and display a second list of contents inside the double clicked item in the overview list (with a back button to return to the overview). I had been thinking about this from the perspective of that overview list displaying the sub contents and how to trigger a NavigationLink from the overview on a double click, but perhaps I need to think about from the sidebar instead?

What I'm thinking is maybe set up a state variable in the sidebar. At a basic level, this would be a boolean defaulting to false (it probably will end up being a struct with a boolean and other info so I know what the context is to display the right data). When the user clicks on an item in the sidebar, the NavigationLink would display the appropriate overview list view, but that overview list view would also have a binding to the state variable from the sidebar. So basically, when tapping the sidebar item it would say, the boolean is false so I display the overview list with a NavigationLink. When the user double clicks an item in the overview list, it would then change the bound variable to true... at which point the sidebar would get the change in state (I think?) and... display the sub view instead of the overview with the NavigationLink (also passing in that binding to the state variable in the sub view). When the user clicks on the back button in the sub view, it would change the boolean back to false... passing that up to the sidebar to once again change the view back to the overview. And if I can figure out animation, maybe I can make it look like the sub view is sliding in and out appropriately to provide the user some context of what is happening too?

Does this approach sound correct or is there some other (easier?) solution I'm perhaps missing that could work in macOS 12 or later that would get me the result I'm trying to achieve?

3      

This is mostly working. Set up an @State in my sidebar for "isShowingOverview" with a default of true. Then have something like this:

NavigationLink(
  destination: {
    if isShowingOverview {
      OverviewView(showingOverview: $isShowingOverview)
    } else {
      VStack {
        Text("Hello World")
      }
      .toolbar {
        DetailToolbar(showingOverview: $isShowingOverview)
      }
      .frame(minWidth: 400, idealWidth: 900, maxWidth: .infinity, minHeight: 400, idealHeight: 600, maxHeight: .infinity)
      .background()
    }
  }, label: {
    Text(category.name)
  }
)

The binding in the OverviewView is working fine. When isShowingOverview is set to false with a double click in my list it then displays my temporary "Hello World" view instead and my DetailToolbar has a back button that then toggles the isShowingOverview back to true and it displays my OverviewView again.

Only thing I'm not sure about. Is there a way I can animate it to look like my placeholder detail view is sliding over the OverviewView (sliding in from the trailing edge to the leading edge) similar to the Mac App Store when you select an item and it displays the detail view and then make it look like my detail view is sliding off the OverviewView (sliding off from the leading edge to the trailing edge) when toggling things back to true from my detail view to provide some additional context for the user that there is some navigation going on rather than the view just appearing?

3      

Not sure entirely, but it appears that because my view is being updated from my sidebar any animation added is ignored in the NavigationLink (or at least everything I have tried hasn't done anything at all except have the view just appear (makes sense as that typically is what happens when you have something selected in the sidebar). I'm not really sure if the animation actually would add much if it was working. I thought it would be somewhat disconcerting to double click my list and just have another view just appear replacing the prior one, but I've added a path to the toolbar title so the user will know where they are, and then there is the back button as well so the context of what happened is actually pretty clear.

3      

Websites like SwiftUI by Example and SwiftUI Lab offer tutorials and examples that cover various aspects of SwiftUI development, including macOS-specific topics. You may also find useful resources on GitHub, where open-source projects or sample code repositories could provide insights into implementing the desired functionality. Exploring these resources should provide you with the guidance and direction you need to achieve the desired result in your SwiftUI macOS app. tiktokdownloads.online

3      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.