Skip to content

Instantly share code, notes, and snippets.

@Dimillian
Created June 10, 2022 08:27
Show Gist options
  • Save Dimillian/c10ffac65a1a37b337a07b5cf0773cea to your computer and use it in GitHub Desktop.
Save Dimillian/c10ffac65a1a37b337a07b5cf0773cea to your computer and use it in GitHub Desktop.
An example on how to use the new NavigationSplitView on iPad with global navigation
import SwiftUI
enum HomeDestination: String, CaseIterable, Hashable {
case hot, best, trending, new, top, rising
}
enum SubredditDestination: String, CaseIterable, Hashable {
case news, diablo, pics, wtf, games, movies
}
enum UserDestination: String, CaseIterable, Hashable {
case profile, inbox, posts, comments, saved
}
enum Destination: Hashable {
case home(home: HomeDestination)
case subreddit(subreddit: SubredditDestination)
case user(user: UserDestination)
case post(post: Post)
}
struct MainView: View {
@State private var sidebarDestination: Destination = .subreddit(subreddit: .games)
@State private var detailNavigation: Destination?
var body: some View {
NavigationSplitView {
SidebarView(destination: $sidebarDestination)
} content: {
switch sidebarDestination {
case .home(let destination):
HomeView(destination: destination)
case .subreddit(let subreddit):
SubredditView(subreddit: subreddit, destination: $detailNavigation)
case .user(let destination):
AccountView(destination: destination)
case .post(let post):
PostView(post: post)
}
} detail: {
NavigationStack {
Group {
if let detailNavigation {
switch detailNavigation {
case .post(let post):
PostView(post: post)
default:
Text("Please select a post")
}
} else {
Text("Please select a post")
}
}.navigationDestination(for: Destination.self) { destination in
switch destination {
case .user(let userDestination):
AccountView(destination: userDestination)
default:
Text("Not supported here")
}
}
}
}
}
}
struct SidebarView: View {
@Binding var destination: Destination
var body: some View {
List(selection: $destination) {
Section("Home") {
ForEach(HomeDestination.allCases, id: \.self) { homeItem in
NavigationLink(value: Destination.home(home: homeItem)) {
Label(homeItem.rawValue.capitalized, systemImage: "globe")
}
}
}
Section("Subreddit") {
ForEach(SubredditDestination.allCases, id: \.self) { subreddit in
NavigationLink(value: Destination.subreddit(subreddit: subreddit)) {
Label(subreddit.rawValue.capitalized, systemImage: "globe")
}
}
}
Section("Account") {
ForEach(UserDestination.allCases, id: \.self) { userDestination in
NavigationLink(value: Destination.user(user: userDestination)) {
Label(userDestination.rawValue.capitalized, systemImage: "globe")
}
}
}
}
}
}
struct Post: Identifiable, Hashable {
let id = UUID()
let title = "A post title"
let preview = "Some wall of text to represent the preview of a post that nobody will read if the title is not a clickbait"
}
struct SubredditView: View {
let subreddit: SubredditDestination
@Binding var destination: Destination?
@State private var posts: [Post] = [Post(), Post(), Post(), Post(), Post(), Post(), Post(), Post()]
var body: some View {
List(posts, selection: $destination) { post in
NavigationLink(value: Destination.post(post: post)) {
HStack {
VStack(alignment: .leading) {
Text(post.title)
.font(.title3)
.fontWeight(.semibold)
Text(post.preview)
.font(.callout)
}
}
}
}.navigationTitle(subreddit.rawValue.capitalized)
}
}
struct PostView: View {
let post: Post
var body: some View {
VStack {
Text(post.title)
.font(.title)
Text(post.preview)
NavigationLink(value: Destination.user(user: .comments)) {
Text("See some sub navigation")
}
}
}
}
struct AccountView: View {
let destination: UserDestination
var body: some View {
Text(destination.rawValue.capitalized)
}
}
struct HomeView: View {
let destination: HomeDestination
var body: some View {
Text(destination.rawValue.capitalized)
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
}
}
@goodmorningbob
Copy link

Hi.

Thanks for posting this, but is it just me or is it not working as expected in beta 1?

When I select an entry in the Sidebar the content view title only updates to reflect the new sidebar selection when you select an entry from the original selected Subredit section. If you select a sidebar entry from, eg. the Home section, the content view does not update?

I had written some similar code and was pulling by hair out over why the content view was not updating...

@SoundBlaster
Copy link

This example (and almost any other implementations of split view) is broken on .compact size class. Looks like an Apple's bug.

@Dimillian
Copy link
Author

Yes, this example actually demo bugs in beta 1

@Jw-44
Copy link

Jw-44 commented Aug 3, 2022

Yes, this example actually demo bugs in beta 1

@nkalvi
Copy link

nkalvi commented Aug 3, 2022

Thanks for posting.
Using @State for selection causes this behaviour in .compact class as sidebar and content are recreated.
So, persisting the selections (by following Bringing robust navigation structure to your SwiftUI app) seems to resolve the issue.

Also, iOS beta 4 resolved the following issue which required a workaround for conditional views:

Fixed: Conditional views in columns of NavigationSplitView fail to update on some state changes. (91311311)

Updated code:
https://gist.github.com/nkalvi/4cdc746ab92b5da664621d61ac7690e9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment