SwiftUI NavigationView

2021년 11월 2일 수정

NavigationView

NavigationView 컴포넌트는 SwiftUI를 이용해 iOS의 네비게이션 컨트롤러(Navigation Controller)나 macOS의 사이드바(Sidebar) UI를 구현할 수 있게 해준다.

기본 사용법

NavigationView 내부에 두 개 이상의 뷰를 선언하는 형태로 구현한다.

NavigationView {
    SidebarView()
    CurrentContentView()
}

이 중 첫 번째가 첫 메뉴 화면 혹은 사이드바 UI가 된다. 그리고 그 이후에 등장하는 뷰들은 다만 macOS의 경우 기본 컨텐츠로 표시된다.

컨텐츠 뷰는 두 개 이상 정의하는 것도 가능하며 macOS에서는 이 경우 컨텐츠 뷰 사이에 구분선(splitter)이 붙는다.

사이드바

사이드바의 메뉴는 NavigationLink 라는 컴포넌트를 통해 정의할 수 있다.

List {
    NavigationLink("First Content", destination: FirstContentView())
}

이 컴포넌트는 여러 생성자가 준비되어 있으니 원하는 방식을 골라서 구현할 수 있다.

대응하는 뷰를 연결할 목적이 아니라면 수동으로 라벨을 나열하는 방법도 있다.

List {
    Label("Item 1", systemImage: "folder")
    Label("Item 2", systemImage: "pencil")
    ...
}

메뉴를 그룹으로 묶기를 원할 때는 Section 으로 묶어줄 수 있다.

List {
    Section(header: Text("First Group")) {
        NavigationLink("First Content", destination: FirstContentView())
        NavigationLink("Second Content", destination: SecondContentView())
    }
    Section(header: Text("Second Group")) {
        NavigationLink("Third Content", destination: ThirdContentView())
        NavigationLink("Fourth Content", destination: FourthContentView())
    }
}

물론 그룹 부분은 SwiftUI 리스트 컴포넌트 쪽과 관련이 있겠지만 말이다.

참고로 NavigationLink 에도 커스텀 뷰를 사용할 수 있다.

NavigationLink(destination: FoobarView()) {
    Text("Foobar")
}

타이틀

타이틀은 Content에 해당하는 뷰에서 세팅하는 형태다.

struct FirstContentView: View {
    var body: some View {
        VStack {
            ...
        }
        .navigationBarTitle(Text("First Content"))
    }
}

타이틀은 여러 옵션이 있을 수 있다. 예를 들자면 이런 식이다.

.navigationBarTitle(Text("Second Content"), displayMode: .inline)

사이드바 스타일

macOS 스타일을 살리기 위해 사이드바를 아래와 같이 세팅할 수도 있다.

List {
    Label("Item 1", systemImage: "folder")
    Label("Item 2", systemImage: "pencil")
    ...
}
.listStyle(SidebarListStyle())
.frame(minWidth: 100, idealWidth: 150, maxWidth: 200, maxHeight: .infinity)

.frame 을 통해 최소 크기를 설정할 수 있다는 점을 참고하자.

툴바

NavigationView 는 툴바로 활용할 수 있는 영역이 있다. 이 부분을 활용해보자.

NavigationView {
    SidebarView()
    .toolbar {
        ToolbarItem(placement: .navigation) {
            Button()
        }
    }

    CurrentContentsView()
    .toolbar {
        ToolbarItem(placement: .automatic) {
            Button()
        }
    }
}

이런 식으로 툴바에 버튼을 넣을 수 있다. placement 의 차이를 잘 살펴보자.

사이드바 토글

macOS 환경의 경우 사이드바는 토글이 가능한 형태인데 이걸 기본적인 보일러플레이트 코드로 제공하지는 않고 있다. 필요하다면 아래 코드로 토글이 가능하다.

NSApp.keyWindow?.firstResponder?
    .tryToPerform(#selector(
        NSSplitViewController.toggleSidebar(_:)), with: nil)

위 코드를 함수로 만들어서 원하는 버튼의 액션으로 호출하면 간단히 토글이 가능해진다.

NavigationView 로 만들어지는 사이드바가 실제로는 NSSplitViewController 를 이용한다는 것을 간접적으로 알 수 있다.