.badge()
ๆนๆณไธ๏ผ.overlay(_:alignment)
extension View {
/// โญ๏ธ add a badge to the view
///
/// Example
/// ```
/// calendarImage
/// .badge(badge, alignment: .topLeading)
/// ```
/// ๆณจๆ๏ผSwiftUI ๅ
งๅปบไนๆ่จฑๅค `.badge(_:)` view modifier๏ผไพๅฆ๏ผ
/// - [`.badge(_ label: Text?)`][1]
/// - [`.badge<S: StringProtocol>(_ label: S?)`][2]
/// - [`.badge(_ key: LocalizedStringKey?)`][3]
/// - [`.badge(_ count: Int)`][4]
///
/// ๆไปฅๅจ่ช่ฃฝ `View` extension ๆ view modifier ็ๆๅ๏ผ่ฆ้ฟๅ
/// ่ๅฎๅ็ธ่กใ
///
/// ๆณจๆ๏ผ้ไบๅ
งๅปบ็ `.badge(_:)` ๅช้ฉ็จๆผ `List` rows ่ iOS tab barsโ๏ธ
///
/// [1]: https://developer.apple.com/documentation/swiftui/navigationview/badge(_:)-3fnhw
/// [2]: https://developer.apple.com/documentation/swiftui/navigationview/badge(_:)-3069i
/// [3]: https://developer.apple.com/documentation/swiftui/navigationview/badge(_:)-cc1t
/// [4]: https://developer.apple.com/documentation/swiftui/navigationview/badge(_:)-57jkr
func badge<Badge: View>(_ badge: Badge, alignment: Alignment = .topTrailing) -> some View {
overlay(alignment: alignment) {
badge.alignmentGuide(alignment.horizontal) { dim in
dim[HorizontalAlignment.center]
}.alignmentGuide(alignment.vertical) { dim in
dim[VerticalAlignment.center]
}
}
}
}
struct CalendarView: View {
var body: some View {
calendarImage
.badge(badge, alignment: .topLeading) // โญ๏ธ
}
var calendarImage: some View {
Image(systemName: "calendar")
.resizable()
.frame(width: 50, height: 50)
.padding()
.background(Color.red)
.cornerRadius(10)
.foregroundColor(.white)
}
var badge: some View {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 60))
.foregroundColor(.green)
.shadow(radius: 4)
}
}
SwiftUI โฉ Layout & Presentation โฉ NavigationView โฉ View Modifiers
func badge(_ key:
LocalizedStringKey
?) -> someView
ๆนๆณไบ๏ผview preference
ๅฉ็จ view preference ไพ้็ฅไธ(ๅค)ๅฑค็ view ่ชๅทฑ็ size๏ผไธฆๅฐ่ชๅทฑ็ view ๆพๅจๅณไธ่งใ
้็ถๅฏไปฅ็จ view preference ไพๆ็๏ผไฝๆฏ่ผๅปบ่ญฐ็ๆนๅผ้ๆฏ็จไธ้ข็ alignmentใ
import SwiftUI
/// ๅ๏ผๅฏไปฅ็จ anchor preference ่ฎ badge ็ๆบๆพๆด็ฐกๅฎๅ๏ผ
/// โญ๏ธ 1. define size preference key:
/// pass view size to its ancestors (by using preference key)
struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
/// โญ๏ธ 2. define a view modifier to set size preference key:
/// get view size and set size preference key
struct SetSizePreference: ViewModifier {
/// body: `ViewModifier` requirement
func body(content: Content) -> some View {
/// โญ๏ธ 2.1 use `GeometryReader` to read view size
content.background(GeometryReader { geo in
/// โญ๏ธ 2.2 use .preference() method to set `SizePreferenceKey`
/// on a transparent background
Color.clear.preference(
key: SizePreferenceKey.self,
value: geo.size
)
})
}
}
/// โญ๏ธ 3. define a convenience extension to set size preference
extension View {
func getSize() -> some View {
modifier(SetSizePreference())
}
}
/// โญ๏ธ 4. define a view modifier to add badge view
struct AddBadge<Badge: View>: ViewModifier {
var badge: Badge
var showBadge: Bool
/// โญ๏ธ 4.1 prepare to read badge size from child's size preference
@State private var badgeSize: CGSize = .zero
func body(content: Content) -> some View {
ZStack(alignment: .topTrailing) {
/// original view
content
/// add a badge to it if `showBadge`
if showBadge {
badge
/// โญ๏ธ 4.2 get badge size and let ancestors know about it
/// by setting size preference.
.getSize()
/// โญ๏ธ 4.3 when get badge size from child's size preference,
/// update view state to reflect the change.
.onPreferenceChange(SizePreferenceKey.self) {
self.badgeSize = $0
}
/// โญ๏ธ 4.4 center badge to the parent's top right corner
/// according to its size.
.offset(x: badgeSize.width / 2, y: -badgeSize.height / 2)
.shadow(radius: 4)
}
}
}
}
/// โญ๏ธ 5. define a convenience extension to add badge
extension View {
func addBadge<Badge: View>(_ badge: Badge,`if` showBadge: Bool) -> some View {
modifier(AddBadge(badge: badge, showBadge: showBadge))
}
}
import SwiftUI
struct CalendarView: View {
var isVerified = true
var body: some View {
calendarImage
/// โญ๏ธ 6. add badge conditionally
.addBadge(badge, if: isVerified)
}
var calendarImage: some View {
Image(systemName: "calendar")
.resizable()
.frame(width: 50, height: 50)
.padding()
.background(Color.red)
.cornerRadius(10)
.foregroundColor(.white)
}
var badge: some View {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 60))
.foregroundColor(.green)
}
}
Sundell โฉ A guide to the SwiftUI layout system - Part 1
Swift with Majid โฉ
Last updated