1. 프로젝트 개설

 

프로젝트명 : NavigationLinkTest 

 

Models.swift 파일 생성

 

 

 

 

 

 

2. Models.swift 파일 작성

 

데이터셋 + 데이터 선언을 합니다.

 

import Foundation

struct MemberModel: Identifiable {
    var id: Int
    var name: String
    var age: Int
    
    init(id: Int, name: String, age: Int) {
        self.id = id
        self.name = name
        self.age = age
    }
}


let members: [MemberModel] = [ MemberModel(id: 1, name: "앤토니", age: 21), MemberModel(id: 2, name: "스티븐", age: 29), MemberModel(id: 0, name: "제임스", age: 23)]

 

 

 

 

 

3. CententView.swift 파일 작성 : 간단하게 List로 구현 예시

 

세부 정보를 보여주는 화면을 그리기 위한 DetailView와, 목록 정보를 보여주는 화면을 그리기 위한 ContentView 작성

 

import SwiftUI


struct ContentView: View {
    var body: some View {
        NavigationView {
            List(members) {member in
                NavigationLink(member.name) {
                    DetailView(person: member)
                }
            }
            .navigationBarTitle("명단")
        }
    }
}

struct DetailView: View {
    var person: MemberModel
    
    var body: some View {
        VStack {
            Image(systemName: "person.circle")
                .resizable()
                .frame(width: 100, height: 100, alignment: .center)
            Text(person.name)
                .font(.system(size: 60))
            Text("\(person.age)세")
                .font(.system(size: 40))
        }
        .navigationTitle("상세내역")
        // NavigationBar에 보이는 Title의 크기를 작게 보이게 설정!
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

정상적으로 작동하는 것을 확인합니다.

NavigationView를 아래와 같이 작성해도 동일하게 작동합니다. A안은 위 코드의 일부이고, B안과 같이 작성해도 동일하게 정상 작동합니다.

 

// A안

struct ContentView: View {
    var body: some View {
        NavigationView {
            List(members) {member in
                NavigationLink(member.name) {
                    DetailView(person: member)
                }
            }
            .navigationBarTitle("명단")
        }
    }
}
// B안

struct ContentView: View {
    var body: some View {
        NavigationView {
            List(members) {member in
                NavigationLink(destination: DetailView(person: member), label: {
                    Text(member.name)
                })
            }
            .navigationBarTitle("명단")
        }
    }
}

 

 

 

 

 

4. CententView.swift 파일 작성 : ForEach로 구현 예시 - 1단계

 

ContentView 구조체 안에 isActive 변수를 선언해주었고, ForEach를 VStack 안에 넣어 구성했습니다.

 

import SwiftUI

struct ContentView: View {
    @State private var isActive: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                ForEach(members, id: \.id) { member in
                    NavigationLink(destination: DetailView(person: member), isActive: $isActive, label: {
                        Text(member.name)
                    })
                }
            }
            .navigationBarTitle("명단")
        }
    }
}

struct DetailView: View {
    var person: MemberModel
    
    var body: some View {
        VStack {
            Image(systemName: "person.circle")
                .resizable()
                .frame(width: 100, height: 100, alignment: .center)
            Text(person.name)
                .font(.system(size: 60))
            Text("\(person.age)세")
                .font(.system(size: 40))
        }
        .navigationTitle("상세내역")
        // NavigationBar에 보이는 Title의 크기를 작게 보이게 설정!
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

 

 

 

 

 

5. CententView.swift 파일 작성 : ForEach로 구현 예시 - 2단계

 

4번 결과에서 특이사항이 있습니다. 우측 하단에 아래와 같은 문구가 출력됩니다.

SwiftUI encountered an issue when pushing a NavigationLink. Please file a bug.

심지어 앤토니를 누르면 앤토니에 대한 상세 정보 화면이 출력되어야하는데, 랜덤으로 다른 정보가 출력된 상세 정보 화면으로 이동하게 됩니다.

 

ForEach마다 NavigationLink를 각각 생산하는 것이 아니라, index만 받아서 NavigationLink를 생성하도록 변경이 필요합니다.

하나의 View에는 하나의 NavigationLink를 넣는 것을 권장합니다.

 

import SwiftUI

struct ContentView: View {
    @State private var isActive: Bool = false
    @State private var selectedMemberModel: MemberModel = MemberModel(id: 0, name: "", age: 0)
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView(person: selectedMemberModel), isActive: $isActive, label: {
                    EmptyView()
                }
                )
                ForEach(members, id: \.id) { member in
                    Spacer().frame(height: 15)
                    Text(member.name)
                        .onTapGesture {
                            selectedMemberModel = member
                            isActive = true
                    }
                }
            }
        }
            .navigationBarTitle("명단")
    }
}

struct DetailView: View {
    var person: MemberModel
    
    var body: some View {
        VStack {
            Image(systemName: "person.circle")
                .resizable()
                .frame(width: 100, height: 100, alignment: .center)
            Text(person.name)
                .font(.system(size: 60))
            Text("\(person.age)세")
                .font(.system(size: 40))
        }
        .navigationTitle("상세내역")
        // NavigationBar에 보이는 Title의 크기를 작게 보이게 설정!
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

 

 

 

 

6. CententView.swift 파일 작성 : ForEach로 구현 예시 - 2단계 : 응용버전

 

단순한 Text형태의 버튼에 디자인을 얹은 예시입니다.

그리고 EmptyView()로 표현되는 부분 또한 다른 뷰를 만들 경우 어떻게 동작하는지 확인하기 위한 예제입니다.

 

기존에 EmptyView()로 작성해두는 곳의 경우, 초기값 기준으로 상세페이지 이동이 됩니다. 그래서 특수한 경우에만 EmptyView()를 미사용하고, 보통은 EmptyView()를 사용할 것 같습니다.

 + 다른 탭을 눌러 상세 페이지로 이동한 후에는, EmptyView()를 보통 넣는 위치에 있는 해당 버튼으로 이동되는 상세 페이지가 초기겂 기준이 아닌, 최근에 들어간 상세페이지 정보로 노출됩니다.

 

import SwiftUI

struct ContentView: View {
    @State private var isActive: Bool = false
    @State private var selectedMemberModel: MemberModel = MemberModel(id: 0, name: "", age: 0)
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView(person: selectedMemberModel), isActive: $isActive, label: {
                    VStack {
                        Text("위에 배치되는 문구로, 처음 누르면 초기값 0으로\n상세페이지 넘어가는데, 다른 페이지 들어간 뒤에는\n해당 페이지로 이동되므로\nEmptyView()를 자주 사용")
                            .foregroundColor(Color.orange)
                        RoundedRectangle(cornerRadius: 20)
                            .frame(width: 150, height: 50)
                            .foregroundColor(Color.pink)
                    }
                }
                )
                ForEach(members, id: \.id) { member in
                    Spacer().frame(height: 15)
                    ZStack {
                        RoundedRectangle(cornerRadius: 20)
                            .frame(width: 100, height: 100)
                            .foregroundColor(Color.gray)
                        Text(member.name)
                    }
                    .onTapGesture {
                        selectedMemberModel = member
                        isActive = true
                    }
                }
            }
        }
    }
}

struct DetailView: View {
    var person: MemberModel
    
    var body: some View {
        VStack {
            Image(systemName: "person.circle")
                .resizable()
                .frame(width: 100, height: 100, alignment: .center)
            Text(person.name)
                .font(.system(size: 60))
            Text("\(person.age)세")
                .font(.system(size: 40))
        }
        .navigationTitle("상세내역")
        // NavigationBar에 보이는 Title의 크기를 작게 보이게 설정!
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

 

 

 

 

 

+ Recent posts