Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Refactoring
- Protocol
- Clean Code
- swiftUI
- SWIFT
- tableView
- collectionview
- ios
- swift documentation
- map
- 애니메이션
- combine
- 리펙터링
- 리펙토링
- ribs
- 클린 코드
- rxswift
- UICollectionView
- UITextView
- HIG
- 스위프트
- Xcode
- uitableview
- 리팩토링
- MVVM
- uiscrollview
- RxCocoa
- Human interface guide
- clean architecture
- Observable
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] 튜토리얼 - 21. iOS앱 프로젝트에 macOS 맥북 앱 UI 구현 방법 (CommandMenu, SidebarCommands, FocusedValues, Preferences 설정) (5) 본문
iOS 튜토리얼 (SwiftUI)
[iOS - SwiftUI] 튜토리얼 - 21. iOS앱 프로젝트에 macOS 맥북 앱 UI 구현 방법 (CommandMenu, SidebarCommands, FocusedValues, Preferences 설정) (5)
jake-kim 2022. 7. 24. 23:15* 프로젝트 파일은 애플 튜토리얼 사이트나 이전 포스팅 글 참고
macOS 실행 시 Sidebar가 들어나지 않는 문제 해결 방법
- macOS를 실행하면 Sidebar를 한 번 닫았을 때 다시 열 수 없는 문제가 존재
- iOS에서 List형태 안에 NavigationLink로 구현하면 macOS에서는 아래처럼 좌측에는 리스트, 우측에는 NavigationLink에 삽입한 화면이 등장
- LandmarkCommands 라는 파일 생성
- LandmarkCommands 안에 SidebarCommands()를 선언
import SwiftUI
struct LandmarkCommands: Commands {
var body: some Commands {
SidebarCommands()
}
}
- LandmarksApp에 .commands를 추가하고, 그 클로저에 LandmarkCommands() 주입
- 해당 코드를 추가하면 상단 View에 메뉴가 생성
@main
struct LandmarksApp: App {
@StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
.commands { // <-
LandmarkCommands()
}
#if os(watchOS)
WKNotificationScene(controller: NotificationController.self, category: "LandmarkNear")
#endif
}
}
- watchOS에는 필요없는 기능이므로 플래그 사용
@main
struct LandmarksApp: App {
@StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
#if !os(watchOS) // <-
.commands {
LandmarkCommands()
}
#endif
#if os(watchOS)
WKNotificationScene(controller: NotificationController.self, category: "LandmarkNear")
#endif
}
}
CommandMenu - 키 설정
- 커멘드 메뉴 구현
- 커멘드는 App을 서브클래싱 하고 있는 곳에서 .commands로 추가
- commands안에는 Commands를 상속받고 인스턴스(LandmarkCommands)를 주입
// LandmarkCommands.swift
struct LandmarkCommands: Commands {
var body: some Commands {
SidebarCommands()
}
}
// LandmarksApp.swift
@main
struct LandmarksApp: App {
@StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
.commands { // <-
LandmarkCommands()
}
...
- 키 정의 - 맥북 상단의 메뉴와 스위프트 코드 연동하기위해 정의
- LandmarkCommands.swift에 FocusedValueKey 키 추가
- SwiftIU 내부에서 정의된 FocusedValues라는 곳에 extension으로 정의한 키를 computed property로 제공하면 키 정의 완료
private struct SelectedLandmarkKey: FocusedValueKey {
typealias Value = Binding<Landmark>
}
extension FocusedValues {
var selectedLandmark: Binding<Landmark>? {
get { self[SelectedLandmarkKey.self] }
set { self[SelectedLandmarkKey.self] = newValue }
}
}
- CommandMunu를 추가하여 Landmark라는 버튼 추가
- 여기에서 커멘드가 눌린 경우, 처리를 해줌
- 사용하는 쪽에서 해당 키값을 선언하고 데이터를 넘기면 처리는 여기서 정의한 코드가 동작하여 데이터에 반영되는 원리
struct LandmarkCommands: Commands {
@FocusedBinding(\.selectedLandmark) var selectedLandmark
var body: some Commands {
SidebarCommands()
CommandMenu("Landmark") { // <-
Button("\(selectedLandmark?.isFavorite == true ? "Remove" : "Mark") as Favorite") {
selectedLandmark?.isFavorite.toggle()
}
.disabled(selectedLandmark == nil)
}
}
}
- 단축키 설정하기
- .keyboardShortcut으로 추가
struct LandmarkCommands: Commands {
@FocusedBinding(\.selectedLandmark) var selectedLandmark
var body: some Commands {
SidebarCommands()
CommandMenu("Landmark") {
Button("\(selectedLandmark?.isFavorite == true ? "Remove" : "Mark") as Favorite") {
selectedLandmark?.isFavorite.toggle()
}
.keyboardShortcut("f", modifiers: [.shift, .option]) // 단축키 설정: `Shift + Option + F`
.disabled(selectedLandmark == nil)
}
}
}
CommandMenu - 키 바인딩
- LandmarkList에 특정 코드를 추가하여 데이터 바인딩
1. selectedLandmark 상태를 기록
- 커멘드 메뉴에서 Mark as Favorite를 선택하면 selectedLandmark에 적용하기 위함
struct LandmarkList: View {
@EnvironmentObject var modelData: ModelData
@State private var showFavoritesOnly = false
@State private var filter = FilterCategory.all
@State private var selectedLandmark: Landmark? // 1.
...
2. index 프로퍼티 추가
- 선택한 아이템의 인덱스값을 리턴하는 프로퍼티
// 2.
var index: Int? {
modelData.landmarks.firstIndex(where: { $0.id == selectedLandmark?.id })
}
3. 기존에 있던 List를 주석처리하고, selection에 위에서 정의한 $selectedLandmark를 주입
- 리스트에서 현재 선택되어 있는 옵션을 부여
var body: some View {
NavigationView {
// List {
List(selection: $selectedLandmark) { // 3.
4. .focusedValue로 위에서 정의했던 키값을 바인딩
- .focusedValue(_:,_:)로, 2가지 값을 주입
- 첫 번째 인수 keyPath에는 위에서 extension FocusedValues에서 정의한 키값을 사용
- 두 번째 인수 value에는 첫번째 인수에 전달할 값
- 두 번째 인수에는 변경될 데이터값을 넣고, 첫 번째 인수에 키패스를 넣으면 키패스에서 정의한 로직대로 데이터가 변경
var body: some View {
NavigationView {
...
Text("Select a Landmark")
}
.focusedValue(\.selectedLandmark, $modelData.landmarks[index ?? 0]) // 4.
}
Preferences, 설정 옵션 추가 방법
- @AppStorage라는 어노테이션을 통해 키값을 설정하여, 맥 설정 바인딩에 사용
- 세팅과 관련된 MapView에 @AppStorage를 사용하여 키값 설정 "MapView.zoom"
- 세팅쪽에도 이와 같은 키값을 사용하고, 이 키값에 해당하는 프로퍼티에 값을 변경하면 여기에도 반영됨
// MapView.swift
struct MapView: View {
var coordinate: CLLocationCoordinate2D
// 키 값 설정
@AppStorage("MapView.zoom")
private var zoom: Zoom = .medium
enum Zoom: String, CaseIterable, Identifiable {
case near = "Near"
...
- 맥을 타겟으로 하는 LandmarkSettings 파일 추가
- 키값을 추가
struct LandmarkSettings: View {
// 키값 정의
@AppStorage("MapView.zoom")
private var zoom: MapView.Zoom = .medium
var body: some View {
Text("Hello, World!")
}
}
struct LandmarkSettings_Previews: PreviewProvider {
static var previews: some View {
LandmarkSettings()
}
}
- 아래 화면처럼, Form을 사용하여 UI 구현
- 이 화면은 커멘드 메뉴에서 Preference를 클릭한 경우 등장하는 화면
struct LandmarkSettings: View {
@AppStorage("MapView.zoom")
private var zoom: MapView.Zoom = .medium
var body: some View {
Form { // <-
Picker("Map Zoom:", selection: $zoom) {
ForEach(MapView.Zoom.allCases) { level in
Text(level.rawValue)
}
}
.pickerStyle(.inline)
}
.frame(width: 300)
.navigationTitle("Landmark Settings")
.padding(80)
}
}
- LandmarkApp에 Setting이라는 인스턴스에 정의한 LandmarkSettings() 주입
@main
struct LandmarksApp: App {
@StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
#if !os(watchOS)
.commands {
LandmarkCommands()
}
#endif
#if os(watchOS)
WKNotificationScene(controller: NotificationController.self, category: "LandmarkNear")
#endif
#if os(macOS) // <-
Settings {
LandmarkSettings()
}
#endif
}
}
* 참고
https://developer.apple.com/documentation/swiftui/focusedvalues
https://developer.apple.com/documentation/swiftui/focusedvaluekey
https://developer.apple.com/tutorials/swiftui/creating-a-macos-app
'iOS 튜토리얼 (SwiftUI)' 카테고리의 다른 글
Comments