πŸ”’ [Swift] μž κΈˆν™”λ©΄ μœ„μ ― λ§Œλ“€κΈ° - WidgetKit

TygerΒ·2024λ…„ 7μ›” 2일
2

Swift with iOS

λͺ©λ‘ 보기
2/3
post-thumbnail

πŸ”’ μž κΈˆν™”λ©΄ μœ„μ ― λ§Œλ“€κΈ° - WidgetKit

WidgetKit | Apple Developer

🍎 WidgetKit μ΄λž€ ?
🎸 Live Activity μ‚¬μš©ν•΄ 보기

이번 κΈ€μ—μ„œλŠ” iOS 16 버전뢀터 λ„μž…λœ 잠금 ν™”λ©΄μ—μ„œ λ³΄μ—¬μ§€λŠ” μœ„μ ―μ„ λ§Œλ“œλŠ” 방법에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄λ €κ³  ν•œλ‹€.

μž κΈˆν™”λ©΄ μœ„μ ―μ€ WidgetKit을 μ‚¬μš©ν•΄μ„œ λ§Œλ“€ 수 있고, κΈ°λŠ₯도 거의 λ™μΌν•˜λ‹€κ³  λ³΄λ©΄λœλ‹€.

이전에 WidgetKit에 λŒ€ν•œ μ „λ°˜μ μΈ λ‚΄μš©μ€ λ‹€λ€˜μ—ˆκΈ° λ•Œλ¬Έμ— 이 κΈ€μ—μ„œλŠ” μ€‘λ³΅λœ λ‚΄μš©μ€ 닀루지 μ•Šμ„ μ˜ˆμ •μ΄λ‹ˆ WidgetKit에 λŒ€ν•΄ λͺ¨λ₯΄μ‹œκ±°λ‚˜ μžμ„Ένžˆ μ•Œκ³  싢은 뢄듀은 μœ„μ— κ³΅μœ ν•œ 글을 μ°Έκ³ ν•˜μ‹œκΈΈ λ°”λžλ‹ˆλ‹€.

ν•΄λ‹Ή κΈ€μ—μ„œλŠ” WidgetKit κ΄€λ ¨ λ‚΄μš©μ„ μž‘μ„±ν•  λ•Œμ— 닀루지 μ•Šμ•˜λ˜ 잠금 ν™”λ©΄ μœ„μ ―μ— λŒ€ν•΄μ„œλ§Œ μž‘μ„±ν•˜λ„λ‘ ν•˜κ² λ‹€.

WidgetKit

WidgetKit은 iOS 14μ—μ„œ λ„μž…λœ μƒˆλ‘œμš΄ ν™˜κ²½μœΌλ‘œ μ‚¬μš©μžκ°€ ν™ˆ ν™”λ©΄μ—μ„œ μ•±μ˜ 컨텐츠λ₯Ό λΉ λ₯΄κ²Œ ν™•μΈν•˜κ³  μƒν˜Έμž‘μš©ν•  수 μžˆλ„λ‘ μΆ”κ°€λœ κΈ°λŠ₯이닀.

WidgetKitμ—μ„œλŠ” iOS 16λΆ€ν„° 잠금 ν™”λ©΄μ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” μƒˆλ‘œμš΄ μœ„μ ―λ“€μ„ μΆ”κ°€ν•˜κ²Œ λ˜μ—ˆλŠ”λ°, 이 κΈ°λŠ₯이 λ°”λ‘œ 이번 κΈ€μ—μ„œ λ‹€λ£¨κ²Œ 될 μœ„μ ―λ“€μ΄λ‹€.

ν™ˆμ—μ„œ μ‚¬μš©ν•˜λŠ” μœ„μ ―κ³Ό 잠금 ν™”λ©΄μ—μ„œ μ‚¬μš©ν•˜λŠ” μœ„μ ―μ€ λ™μΌν•˜κ²Œ κ΅¬μ„±λ˜μ–΄ 있기 λ•Œλ¬Έμ— μ‚¬μš© 방법은 크게 λ‹€λ₯΄μ§€ μ•Šλ‹€.

Home Widget Lock Screen Widget

WidgetKit의 핡심 ꡬ성 μš”μ†ŒμΈ TimelineProvider, Entity, Timeline을 λͺ¨λ‘ κ³΅μœ ν•˜κ³  있기 λ•Œλ¬Έμ— μ‚¬μš© 방법은 μ™„μ „νžˆ λ™μΌν•˜λ‹€κ³  보면 λœλ‹€.

Widget Extension을 μΆ”κ°€ν•  λ•Œμ— μ‚¬μš©ν•˜λŠ” StaticConfiguration, IntentConfiguration 섀정은 ν™ˆ μœ„μ ―μ—μ„œλ§Œ κΈ°λŠ₯이 λ‹€λ₯΄κ²Œ 되고 잠금 ν™”λ©΄ μœ„μ ―μ—μ„œλŠ” 차이가 μ—†λ‹€κ³  보면 λœλ‹€.
잠금 ν™”λ©΄ μœ„μ ―μ€ νŽΈμ§‘μ΄ λΆˆκ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

WidgetKit의 μ’…λ₯˜ 및 ꡬ성 μš”μ†Œμ— λŒ€ν•΄μ„œ ν™•μ‹€νžˆ 이해λ₯Ό ν•˜κ³  μžˆμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 이해가 μ•ˆλ˜μ‹ λ‹€λ©΄ μœ„μ— κ³΅μœ ν•œ 글을 μ½μ–΄λ³΄μ‹œκΈΈ ꢌμž₯ν•©λ‹ˆλ‹€.

Lock Screen Widget

이제 본격적으둜 잠금 ν™”λ©΄ μœ„μ ―μ„ μ‚¬μš©ν•΄ 보자.

잠금 ν™”λ©΄μ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” μœ„μ ―μ€ accessoryInline, accessoryRectangular, accessoryCircular μ΄λ ‡κ²Œ 3가지 μ’…λ₯˜κ°€ 있고, 각각 ν™”λ©΄μ—μ„œ 보여쀄 수 μžˆλŠ” μ‚¬μ΄μ¦ˆκ°€ μ œν•œλ˜μ–΄ μžˆλ‹€.

accessoryInline accessoryRectangular accessoryCircular

Widget Extension을 μΆ”κ°€ν•˜μž.

File > Target

μ›ν•˜λŠ” μœ„μ ―μ˜ 이름을 μ •ν•˜κ³  λ§Œλ“€μ–΄ μ£Όλ©΄ λ˜λŠ”λ° "include Configuration App Intent" μ„ νƒν•˜κ³  μƒμ„±ν•˜λ©΄ IntentConfiguration μœ„μ ―μœΌλ‘œ μƒμ„±λ˜κ³  ν•΄μ œν•˜κ²Œ 되면 StaticConfiguration으둜 생성이 λœλ‹€.

Live Activityκ°€ ν•„μš”ν•œ 경우라면 μ²΄ν¬ν•΄μ„œ 생성해주면 λœλ‹€.

Families

이제 잠금 ν™”λ©΄ μœ„μ ― μ‚¬μš©μ„ μœ„ν•΄ μ‚¬μš©ν•˜κ³ μž ν•˜λŠ” μœ„μ ― νƒ€μž…λ“€μ„ 좔가해보도둝 ν•˜μž.

만일 μΆ”κ°€ν•˜μ§€ μ•Šκ³  κΈ°λ³Έ μƒνƒœλ‘œ λ‘”λ‹€λ©΄ 잠금 ν™”λ©΄ μœ„μ ―μ„ μ œμ™Έν•œ μœ„μ ―λ“€λ§Œ 생성이 λœλ‹€.

supportedFamiliesλ₯Ό μ‚¬μš©ν•΄ μ›ν•˜λŠ” μœ„μ ― νƒ€μž…λ“€μ„ λ°°μ—΄ ν˜•νƒœλ‘œ λ„£μ–΄μ£Όλ©΄ λœλ‹€.

ν•„μš”ν•œ 잠금 ν™”λ©΄ μœ„μ ―λ“€μ„ λ„£μ–΄μ£Όλ©΄ λœλ‹€.

supportedFamilies([.accessoryInline, .accessoryCircular, .accessoryRectangular])

ν™ˆ μœ„μ ―λ„ ν•¨κ»˜ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ ν™ˆ μœ„μ ―μ˜ μ‚¬μ΄μ¦ˆλ„ μΆ”κ°€ν•΄μ£Όλ©΄ λœλ‹€.

supportedFamilies([.systemSmall, .systemMedium, .accessoryInline, .accessoryCircular, .accessoryRectangular])

ν˜„μž¬ μ„œλΉ„μŠ€κ°€ iOS 16 μ΄ν•˜λ₯Ό νƒ€κ²Ÿν•˜κ²Œ 되면 μž κΈˆν™”λ©΄ μœ„μ ―μ„ μ‚¬μš©ν•  수 μ—†κΈ° λ•Œλ¬Έμ— λΆ„κΈ° 처리λ₯Ό ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.

private let supportedFamilies: [WidgetFamily] = {
	if #available(iOS 16.0, *) {
		return [.systemSmall, .accessoryCircular, .accessoryInline, .accessoryRectangular]
	} else {
		return [.systemSmall]
	}
}()

Viewλ₯Ό 뢄리해 쀄 ν•„μš”κ°€ μžˆλŠ”λ°, ν™ˆ ν™”λ©΄μ—μ„œ μ‚¬μš©ν•  μœ„μ ―κ³Ό 잠금 ν™”λ©΄μ˜ μœ„μ ―λ“€μ˜ μ‚¬μ΄μ¦ˆμ˜ 차이가 많기 λ•Œλ¬Έμ— UI ꡬ성을 λ³„λ„λ‘œ ν•΄μ£ΌλŠ” 것이 μ’‹λ‹€.

ν™˜κ²½ λ³€μˆ˜λ‘œ widgetFamilyλ₯Ό μ‚¬μš©ν•΄ μ‚¬μ΄μ¦ˆλ₯Ό κ°€μ Έμ˜¬ 수 μžˆλ‹€.

struct LockScreenWidgetEntryView : View {
    
    @Environment(\.widgetFamily) private var widgetFamily
    
    var entry: Provider.Entry
    
    var body: some View {
        switch widgetFamily {
        case .accessoryInline:
            InlineView()
        case .accessoryCircular:
            CircularView()
        case .accessoryRectangular:
            RectangularView()
        case .systemSmall:
            SmallView()
        default:
            EmptyView()
        }
    }
}

AccessoryWidgetBackground

μœ„μ ―μ˜ UI 쀑에 μ–΄λ–€ μœ„μ ―μ€ λ°±κ·ΈλΌμš΄λ“œ μ˜μ—­μ΄ 있고, μ–΄λ–€ μœ„μ ―μ€ λ°±κ·ΈλΌμš΄λ“œ μ˜μ—­μ΄ 보여지지 μ•Šμ„ 것이닀.

잠금 ν™”λ©΄ μœ„μ ―μ—μ„œλ§Œ μ‚¬μš©ν•˜λŠ” AccessoryWidgetBackground 객체λ₯Ό μ‚¬μš©ν•΄ λ°±κ·ΈλΌμš΄λ“œ μ˜μ—­μ„ μΆ”κ°€ν•  수 μžˆλ‹€.

ZStack을 μ‚¬μš©ν•΄μ„œ λ°±κ·ΈλΌμš΄λ“œλ₯Ό μΆ”κ°€ν•΄μ£Όλ©΄ λœλ‹€.

struct CircularView : View {
    var body: some View {
        ZStack {
            AccessoryWidgetBackground()
            Text("Sample")
        }
    }
}

accessoryCircular νƒ€μž…μ€ μ›ν˜•μ΄κΈ° λ•Œλ¬Έμ— λ°±κ·ΈλΌμš΄λ“œλ₯Ό 좔가해도 λ ˆλ””μš°μŠ€λ₯Ό λ³„λ„λ‘œ μ„€μ •ν•  ν•„μš”κ°€ μ—†μ§€λ§Œ accessoryRectangular νƒ€μž…μ— λ°±κ·ΈλΌμš΄λ“œ μ˜μ—­μ„ μΆ”κ°€ν•˜κ³  μ‹Άλ‹€λ©΄ λ ˆλ””μš°μŠ€μ— λŒ€ν•œ 값도 μΆ”κ°€λ‘œ 지정해 μ£Όμ–΄μ•Ό ν•œλ‹€.

cornerRadiusλ₯Ό μ‚¬μš©ν•΄ μ›ν•˜λŠ” λ ˆλ””μš°μŠ€λ₯Ό μ„€μ •ν•  수 μžˆλ‹€.

AccessoryWidgetBackground().cornerRadius(12)

Content Truncation

μ΄λ²ˆμ—λŠ” λ””λ°”μ΄μŠ€ μ‚¬μ΄μ¦ˆμ— μ˜ν•΄ μ›ν•˜λŠ” μœ„μ ―μ΄ μž˜λ¦¬λŠ” 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄λ„λ‘ ν•˜κ² λ‹€.

λ³΄μ—¬μ£Όκ³ μž ν–ˆλ˜ UI 보닀 λ””λ°”μ΄μŠ€ μ‚¬μ΄μ¦ˆκ°€ μž‘μ•„ μ€„μž„ν‘œ ν‘œμ‹œκ°€λ  μˆ˜λ„ μžˆλŠ” λ¬Έμ œκ°€ μžˆλ‹€.

전체 UIκ°€ 화면에 보여지지 μ•Šκ³  μž˜λ¦¬λŠ” 경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ λŒ€μ²΄ μœ„μ ―μ„ 미리 μΆ”κ°€ν•˜μ—¬ λ””λ°”μ΄μŠ€κ°€ 보여쀄 수 μžˆλŠ” μ˜μ—­μ— λ§žλŠ” μ‚¬μ΄μ¦ˆμ˜ μœ„μ ―μ„ μžλ™μœΌλ‘œ λ…ΈμΆœ μ‹œν‚€λ„λ‘ μ§€μ›ν•˜κ³  μžˆλ‹€.

15 Pro Max 15 Pro

μœ„μ— 15 Pro Max와 15 Pro의 μ‚¬μ΄μ¦ˆ 차이둜 μΈν•˜μ—¬ accessoryInline에 λ…ΈμΆœλ˜λŠ” λ¬Έμžκ°€ λ‹€λ₯Έ 것을 확인할 수 μžˆλ‹€.

ViewThatFitsλ₯Ό μ‚¬μš©ν•΄ 미리 λŒ€μ²΄ μœ„μ ―λ“€μ„ μΆ”κ°€ν•΄ 두면 λͺ¨λ“  λ””λ°”μ΄μŠ€ μ‚¬μ΄μ¦ˆμ— μ μ ˆν•œ UIλ₯Ό 보여쀄 수 있게 λœλ‹€.

struct InlineView : View {
    var body: some View {
        ViewThatFits {
            Text("Lock screen widget test 🌈")
            Text("Lock screen test πŸ”₯")
            Text("Lock screen πŸš€")
        }
    }
}

Refresh

μœ„μ ―μ˜ 데이터λ₯Ό λ¦¬ν”„λ ˆμ‹œν•˜λŠ” 방법에 λŒ€ν•΄μ„œλŠ” 이전에 μž‘μ„±ν•œ WidgetKit의 μ‚¬μš© 방법과 λ™μΌν•˜λ‹ˆ ν•΄λ‹Ή κΈ€ μ°Έκ³ ν•˜μ…”μ„œ TimelineProvider, Timeline, Entry둜 κ΅¬μ„±λœ μš”μ†Œλ₯Ό μ‚¬μš©ν•˜μ‹œλ©΄ 되고, WidgetCenterλ₯Ό μ‚¬μš©ν•œ μ¦‰μ‹œ μ—…λ°μ΄νŠΈμ— λŒ€ν•΄μ„œλ„ 확인할 수 μžˆλ‹€.

μ—¬κΈ°μ—λŠ” λ³„λ„λ‘œ μ‚¬μš© 방법에 λŒ€ν•΄μ„œλŠ” μž‘μ„±ν•˜μ§€ μ•Šλ„λ‘ ν•˜κ² λ‹€.

마무리

WidgetKit을 μ‚¬μš©ν•΄ 잠금 ν™”λ©΄ μœ„μ ―μ„ λ§Œλ“œλŠ” 방법에 λŒ€ν•΄μ„œ κ°„λ‹¨ν•˜κ²Œ μ•Œμ•„λ³΄μ•˜λ‹€.

κΆκΈˆν•˜μ‹  사항 μžˆκ±°λ‚˜ 잘 λͺ» μž‘μ„±λœ λ‚΄μš©μ΄ μžˆλ‹€λ©΄ λŒ“κΈ€ λ‚¨κ²¨μ£Όμ„Έμš” !

κ°μ‚¬ν•©λ‹ˆλ‹€.

profile
Flutter Developer

0개의 λŒ“κΈ€

κ΄€λ ¨ μ±„μš© 정보