Post

SwiftUI Link와 네비게이션

SwiftUI Link와 네비게이션

SwiftUI의 List는 수직 방향의 리스트 형태로 정적 동적 데이터를 모두 표현할 수 있고 추가, 삭제, 순서 재정렬 기능을 수행한다 또한 터치했을때 다른 영역으로 이동이 가능한 Navigationstack컴포넌트와 NavigationLink컴포넌트를 이용한다

 

List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import SwiftUI

struct ContentView: View {
    var body: some View {
        List {
            HStack {
                Image (systemName: "trash.circle.fill")
                Text("Take out the trash")
            }
            HStack {
                Image (systemName: "person.2.fill")
                Text("Pick up the kids")
            }
            HStack {
                Image(systemName: "car.fill")
                Text ("Wash the car")
            }
        }
    }
}

위 코드처럼 하나의 리스트 항목에 추가로 뷰를 넣을 수 있다

 

동적 리스트

시간에 따라 변하는 항목을 포함한다면 동적 리스트라고 할 수 있다 이런 변화를 반영할려면 업데이트를 해야한다 이런 타입의 리스트를 지원할려면 표시될 데이터는 Identifiable 프로토콜을 따르는 클래스 또는 구조체를 포함해야한다 또한 이 프로토콜을 사용할려면 각 항목을 고유하게 식별할 수 있는 id프로퍼티를 가져야한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import SwiftUI

struct ToDoItem: Identifiable {
    var id = UUID()
    var task: String
    var imageName: String
}

struct ContentView: View {
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        List(listData) { Item in
            HStack {
                Image (systemName: Item.imageName)
                Text(Item.task)
            }
        }
    }
}

이렇게 표시가 된다 만약 여기에 정적인 부분이 추가적으로 들어가야된다면 동적리스트 구문을 ForEach구문을 사용하면 된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import SwiftUI

struct ToDoItem: Identifiable {
    var id = UUID()
    var task: String
    var imageName: String
}

struct ContentView: View {
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        List {
            Section(header: Text("To Do List")){
                Button(action: {
                    listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
                }) {
                    Text("add item")
                }
            }
            
            Section(header: Text("To Do List2")){
                ForEach(listData) { Item in
                    HStack {
                        Image (systemName: Item.imageName)
                        Text(Item.task)
                    }
                }
            }
        }
    }
}

정적 부분(버튼 부분)과 동적 데이터가 들어가는 부분을 나누고 또한 Section을 추가해서 리스트 내부에서 구별을 할 수 있도록 했다 사진에서는 add item을 눌러 buy groceries을 추가했다

 

새로고침 기능

아이폰에서 화면을 맨위로 올리면 현재 화면이 새로 고침된다 리스트에서 이 기능을 활성화 할 수 있다 .refreshable을 사용한다

1
2
3
4
5
6
7
8
9
10
List {
            // 생략
        }
        .refreshable {
            listData = [
                ToDoItem (task: "Order dinner", imageName: "dollarsign.circle.fill"),
                ToDoItem (task: "Call financial advisor", imageName: "phone.fill"),
                ToDoItem (task: "Sell the kids", imageName: "person.2.fi11")
            ]
        }

화면을 맨위로 옮기면 리스트 데이터들이 새로 작성한 데이터로 교체된다

 

리스트에 있는 항목을 터치해서 이동하고 싶다면 리스트를 NavigationStack에 넣고 각행을 NavigationLink으로 감싼다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct ContentView: View {
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        NavigationStack {
            List {
                Section(header: Text("To Do List")){
                    Button(action: {
                        listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
                    }) {
                        Text("add item")
                    }
                }
                
                Section(header: Text("To Do List2")){
                    ForEach(listData) { Item in
                        NavigationLink(value: Item.task){
                            HStack {
                                Image (systemName: Item.imageName)
                                Text(Item.task)
                            }
                        }
                    }
                }
            }
            .navigationDestination(for: String.self) { task in
                Text("Selected task = \(task)")
            }
        }
    }
}

리스트 행을 클릭한다면 해당 리스트가 가진 문자열을 새로운 뷰화면에서 보여준다

 

네비게이션 경로

NavigationStack은 이름 그대로 스택을 제공한다 NavigationLink로 이동한다면 스택에 푸시가 되는것이다 사용자는 이렇게 들어온 뷰를 pop할 수 있는데 위 사진에서 보이는 Back버튼을 통해서 pop 할 수 있다 이렇게 사용자가 이동하는 경로는 네비게이션 경로라고 한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct ContentView: View {
    
    @State private var stackPath = NavigationPath() //경로
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List {
                Section(header: Text("To Do List")){
                    Button(action: {
                        listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
                    }) {
                        Text("add item")
                    }
                    Text("\(stackPath) items")
                }
                
                Section(header: Text("To Do List2")){
                    ForEach(listData) { Item in
                        NavigationLink(value: Item.task){
                            HStack {
                                Image (systemName: Item.imageName)
                                Text(Item.task)
                            }
                        }
                    }
                }
            }
            .navigationDestination(for: String.self) { task in
                Text("Selected task = \(task)")
                Text("\(stackPath)")
                Text("\(stackPath.count) items")
                Button("Go back") {
                    stackPath.removeLast()
                }
            }
        }
    }
}

경로를 stackPath에 저장해서 사용할 수 있다 이를 이용하면 하나씩 되돌아가는것이 아니라 한번에 돌아가거나 원하는 페이지로 한번에 이동할 수 있다

add item 밑에는 현재 저장된 경로를 띄웠고 리스트중 2번째 행을 클릭했을때 결과를 오른쪽에 표시했다 경로 저장 형식과 몇단계까지 들어왔는지 확인할 수 있다 여기서는 1단계라 1로 표시 go back버튼은 경로속에 있는것을 초기화시켜서 돌아간다

stackPath.append(value)를 통해서 원하는 곳으로 바로 이동할 수 있다

 

네비게이션 바 커스텀

1
2
3
4
5
6
.navigationBarTitle(Text("To Do List"))
            .navigationBarItems(leading: Button(action: {
                listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
            }) {
            Text ("Add")
            })

이 부분을 추가해서 네이게이션 바에 이름과 추가 버튼을 생성

 

편집 기능

위에서는 사용자가 리스트 항목을 추가했었다 편집을 할려면 리스트 순서 변경 삭제 기능이 필요하다 삭제 기능은 각 행에 onDelete()수정자를 추가한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct ContentView: View {
    
    @State private var stackPath = NavigationPath()
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List {
                Section(header: Text("To Do List")){
                    Button(action: {
                        listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
                    }) {
                        Text("add item")
                    }
                }
                
                Section(header: Text("To Do List2")){
                    ForEach(listData) { Item in
                        NavigationLink(value: Item.task){
                            HStack {
                                Image (systemName: Item.imageName)
                                Text(Item.task)
                            }
                        }
                    }.onDelete(perform: deleteItem)
                }
            }
            .navigationDestination(for: String.self) { task in
                Text("Selected task = \(task)")
                Button("Go back") {
                    stackPath.removeLast()
                }
            }
        }
    }
    func deleteItem(at indexSet: IndexSet) {
        listData.remove(atOffsets: indexSet)
    }
}

항목을 이동시키기 위해서는 onMove()수정자를 사용해야한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct ContentView: View {
    
    @State private var stackPath = NavigationPath()
    
    @State var listData: [ToDoItem] = [
        ToDoItem(task: "Take out trash", imageName: "trash.circle.f111"),
        ToDoItem(task: "Pick up the kids", imageName: "person.2.f111"),
        ToDoItem(task: "Wash the car", imageName: "car.fi11")
    ]
    
    var body: some View {
        NavigationStack(path: $stackPath) {
            List {
                Section(header: Text("To Do List")){
                    Button(action: {
                        listData.append(ToDoItem(task: "Buy groceries", imageName: "cart.badge.plus"))
                    }) {
                        Text("add item")
                    }
                }
                
                Section(header: Text("To Do List2")){
                    ForEach(listData) { Item in
                        NavigationLink(value: Item.task){
                            HStack {
                                Image (systemName: Item.imageName)
                                Text(Item.task)
                            }
                        }
                    }.onDelete(perform: deleteItem)
                        .onMove(perform: moveItem)
                }
            }
            .toolbar { EditButton() // 수정 버튼 추가
                        }
            .navigationDestination(for: String.self) { task in
                Text("Selected task = \(task)")
                Button("Go back") {
                    stackPath.removeLast()
                }
            }
        }
    }
    func deleteItem(at indexSet: IndexSet) {
        listData.remove(atOffsets: indexSet)
    }
    func moveItem(from source: IndexSet, to destination: Int) {
        listData.move(fromOffsets: source, toOffset: destination)
    }

}

오른쪽 위 edit버튼을 누른 뒤 수정이 가능하다

This post is licensed under CC BY 4.0 by the author.