SwiftUI) Podcasts clone coding - Menu
내용
- SwiftUI 사용해서 Podcasts 앱의 클론코딩을 진행했다.
- 아래의 Menu 를 구현해보자!
해당 컴포넌트는 Menu
이다. 개발자 문서를 통해서 알아보자.
Menu
A control for presenting a menu of actions.
Overview
다음 코드는 세 개의 버튼으로 구성된 Menu 와 세 개의 버튼을 포함하는 하위 메뉴를 보여줍니다.
// action 은 커스텀 메서드입니다.
Menu("Actions") {
Button("Duplicate", action: duplicate)
Button("Rename", action: rename)
Button("Delete…", action: delete)
Menu("Copy") {
Button("Copy", action: copy)
Button("Copy Formatted", action: copyFormatted)
Button("Copy Library Path", action: copyPath)
}
}
menu 타이틀을 이전 예시처럼 ✅ (참고)LocalizedStringKey(문자열의 localization) 를 사용해서 만들거나, image 나 text 뷰 같은 다수의 뷰를 만드는 view builder 를 사용해서 만들 수 있습니다.
Menu {
Button("Open in Preview", action: openInPreview)
Button("Save as PDF", action: saveAsPDF)
} label: {
Label("PDF", systemImage: "doc.fill")
}
Primary Action
Menu 는 커스텀 primary action
으로 만들 수 있습니다. primary action
은 사용자가 control 의 body 를 taps 혹은 clicks 했을 때 수행되고 menu presentation
(위와 같은 메뉴를 보여주는 기능) 은 long press 혹은 ✅ (참고)menu indicator
를 click 하는 것과 같은 보조 제스처에서 발생합니다.
다음 코드는 menu 에 표시되는 advanced options 와 함께 책갈피를 추가하는 menu 를 만드는 예시입니다.
Menu {
// 🔥 long press 혹은 menu indicator 를 click 해야 등장한다.
Button(action: addCurrentTabToReadingList) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: bookmarkAll) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: show) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} primaryAction: {
// 🔥 taps 혹은 clicks.
addBookmark()
}
🚨 에러
🙆♂️ 해결: if #available(iOS 15.0, *)
iOS 15 이상부터 primaryAction 을 사용할 수 있었고, #available 를 사용해서 분기처리 해주었습니다.
// 🔥 iOS 15 이상부터 사용가능.
if #available(iOS 15.0, *) {
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} primaryAction: {
print("primary action")
}
} else {
// Fallback on earlier versions
}
Styling Menus
[menuStyle(:)](https://developer.apple.com/documentation/swiftui/view/menustyle(:))modifier 를 사용해서 메뉴의 스타일을 변경할 수 있습니다. 아래의 코드는 custom style 을 적용하는 방법을 보여줍니다.
Menu("Editing") {
Button("Set In Point", action: setInPoint)
Button("Set Out Point", action: setOutPoint)
}
// custom style
.menuStyle(EditingControlsMenuStyle())
참고
✅ LocalizedKeyString
- LocalizedStringKey 인스턴스를 만들어서 문자열을 localize 할 수 있습니다.
UIKit 에서 UILabel
은 String 로 초기화를 합니다. 그러나 SwiftUI 에서 UILabel 역할을 하는 Text
는 LocalizedKeyString
을 통해서 초기화를 합니다. 즉, NSLocalizedString
를 사용하지않아도 자동으로 localization 되는 것입니다.
Menu
역시 LocalizedStringKey
를 사용해서 초기화 할 수 있습니다.
✅ menu indicator
primary action
을 사용하게 되면 menu presentation 은 long press 와 menu indicator
를 통해서 기능한다고 했습니다.
**menuIndicator(_:)**
Sets the menu indicator visibility for controls within this view.
func menuIndicator(_ visibility: Visibility) -> some View
위의 modifier 를 통해서 menu indicator 가 있는 Menu 를 만들 수 있습니다.
- 화살표와 같은 indicator 가 등장한다고 하는데 왜인지.. 등장하지 않았습니다..!
Menu {
// long press 혹은 menu indicator 를 click 해야 등장한다.
Button(action: addCurrentTabToReadingList) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: bookmarkAll) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: show) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} primaryAction: {
addBookmark()
}
// 🔥 menuindicator modifier 를 통해서 가시성을 설정할 수 있다.
.menuIndicator(.visible)
[Apple Developer Documentation - menuindicator(:)](https://developer.apple.com/documentation/swiftui/link/menuindicator(:))
클론코딩
- Podcast 클론코딩을 진행해보자.
Menu {
Button(action: {
print("Download Episode")
}) {
Label("Download Episode", systemImage: "arrow.down.circle")
}
Button(action: {
print("Play Episode")
}) {
Label("Play Episode", systemImage: "play")
}
Divider()
Button(action: {
print("Play Next")
}) {
Label("Play Next", systemImage: "text.insert")
}
Button(action: {
print("Play Last")
}) {
Label("Play Last", systemImage: "text.append")
}
Button(action: {
print("Go to Show")
}) {
Label("Go to Show", systemImage: "airplayaudio")
}
Button(action: {
print("Save Episode")
}) {
Label("Save Episode", systemImage: "bookmark")
}
Divider()
Button(action: {
print("Report a Concern")
}) {
Label("Report a Concern", systemImage: "exclamationmark.bubble")
}
Button(action: {
print("Copy Link")
}) {
Label("Copy Link", systemImage: "link")
}
Button(action: {
print("Share Episode...")
}) {
Label("Share Episode...", systemImage: "square.and.arrow.up")
}
} label: {
Image(systemName: "ellipsis")
.foregroundColor(.secondary)
}
에러: Extra argument in call
Podcast 의 Menu 안에는 10개 넘는 Button 들이 필요했고, 다음과 같은 에러를 만나게 되었다. SwiftUI 에서는 View Builder 안에 최대 10개까지만 선언 할 수 있다.
해결: 다음과 같이 파일분할하여 진행하였다.
Divider 를 통해서 구분지었는데 해당 영역마다 역할이 있다면 해당 이름으로 지어도 되지만 순서를 나타내기 위해서 다음과 같이 작성했다.
Menu {
ThrdMenu()
Divider()
SecondMenu()
Divider()
FirstMenu()
} label: {
Image(systemName: "ellipsis")
.foregroundColor(.secondary)
}
// FristMenu
// Divider
// SecondMenu
// Divider
// ThirdMenu
// 순서로 뷰에 그려진다.
결과
(Go to Show 의 아이콘을 SF Symbol 에서 찾지 못했다..ㅜ 그래서 비슷한걸로 대체했습니당)