์๋ ํ์ญ๋๊น, ๋ฏผํธ์ ๋๋ค. ๐
์คํฐ๋ ์ค SwiftUI์ InteractiveUI๋ค์ ๋ํด ์ ๋ฆฌํด ๋ณด์์ต๋๋ค.
1. Gesture
@GestureState
- ์ ์ค์ฒ๊ฐ ์งํ ์ค์ผ ๋๋ง ์์์ ์ผ๋ก ์ํ๋ฅผ ์ ์ง
- ์ ์ค์ฒ๊ฐ ๋๋๋ฉด ์๋์ผ๋ก ์ด๊ธฐ๊ฐ์ผ๋ก ๋๋์๊ฐ๋ ์ํ ๋ํผ (์ผ์์ )
- ์ํ์ ๋ฐ๋ผ ๋ทฐ ์์ฑ์ ์ค์๊ฐ์ผ๋ก ๋ณํ์ํด
- @State ์ฒ๋ผ ์ง์์ ์ธ ๋ณํ๊ฐ ์๋, ์์ ์ํ ์ฉ๋
- ์ ์ค์ณ ์ถ์ ํ๋ ๋์ ์ฝ๋ฐฑ์ ํตํด ์ํ๋ฅผ ์ง์ ๊ฐฑ์
- updating: ์ ์ค์ณ๊ฐ ๋ณ๊ฒฝ๋๋ ์๊ฐ, ์ผ์์ ์ธ UI ์ํ
- onChanged: ์ ์ค์ณ๋ฅผ ์ฌ์ฉํ๋ ๋์์ UI ์ํ, ์ข ๋ฃ ํ ์ฌ์ค์ X
- onEnded: ์ข ๋ฃ ํ ์ํ ์ฌ์ค์
Gesture Composition Type
- Simultaneous
- ์ฌ๋ฌ ์ ์ค์ณ๋ฅผ ํฉ์ณ์ ํ ๋ฒ์ ์ธ์
- Sequenced
- ์ฌ๋ฌ ์ ์ค์ณ ์ํ๋ฅผ ์์๋๋ก ์ ์ฉ ๊ฐ๋ฅ
- Exclusive
- ์ฌ๋ฌ ์ ์ค์ณ ์ค ์ฒ์ ์๋ํ ํ๋์ ์ ์ค์ณ๋ง ํ์ฑํ
์์ ์ฝ๋
import SwiftUI
struct GestureView: View {
@GestureState private var isPressed = false
@State private var offset = CGSize.zero
var body: some View {
Circle()
.fill(isPressed ? Color.blue : Color.red)
.frame(width: 100, height: 100)
.offset(offset)
.gesture(
LongPressGesture(minimumDuration: 0.5)
.sequenced(before: DragGesture())
.updating($isPressed) { value, state, _ in
if case .first(true) = value {
state = true
}
}
.onEnded { value in
if case .second(true, let drag?) = value {
offset = drag.translation
}
}
)
}
}
2. Animation
- Apple์ด SwiftUI๋ฅผ ๋ง๋ค๊ฒ ๋ ์ค์ํ ๊ณ๊ธฐ ์ค ํ๋
- State์ ๋ณํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์
๋ช ์์ vs ์๋ฌต์
- ๋ช
์์
- ๊ฐ๋ฐ์๊ฐ ์ง์ withAnimation ๋ธ๋ก ์์์ ์ํ ๋ณ๊ฒฝ ํธ๋ฆฌ๊ฑฐ
- ์ ์ด ์์ค ๋์ (์ ํํ ์์น์ ์ ๋๋ฉ์ด์ ์ ์ฉ)
- ์ ์ธ์ ์ด๋ ์๋ ๋ช ํ
- ์ฌ์ฉ์๊ฐ ์ ์ดํด์ผ ํ ์ํธ์์ฉ, ์ ํ์ ์ ํฉ
- ์์์
- ๋ทฐ์ .animation modifier๋ฅผ ์ง์ ํ๋ฉด ํน์ ์ํ๊ฐ์ ๋ณํ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ ์ฉ
- ์ ์ด ์์ค ๋ฎ์ (์ ์ฒด ๋ทฐ์ ์ ๋๋ฉ์ด์ ์ ์ฉ๋จ)
- ์ ์ธ์ ์ผ๋ก ๊ฐ๋จํ๋ ์์ธก์ด ์ด๋ ค์ธ ์๋ ์์.
- ๋จ์ํ UI ์ํ ๋ณํ
matchedGeometryEffect
- ๋ ๊ฐ์ ๋ทฐ๊ฐ ์๋ก ๋ค๋ฅธ ์์น๋ ํฌ๊ธฐ๋ผ๋ ์์คํ ์ด ์ด๋ค์ ๋์ผํ ๋ทฐ๋ก ์ธ์ํ๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ
- ๋ทฐ ๊ฐ ์ ํ ์ ์์น, ํฌ๊ธฐ, ๋ชจ์ ๋ฑ์ ์์ฐ์ค๋ฝ๊ฒ ์ ๋๋ฉ์ด์ ์ผ๋ก ์ฐ๊ฒฐํ ์ ์๊ฒ ๋ง๋ค์ด์ค.
- ์์ ๋ทฐ์ ํฐ ๋ทฐ๋ ์ ํ ๋ค๋ฅธ ๋ทฐ์ด์ง๋ง, ํ๋์ ๋ทฐ์ฒ๋ผ ์ ๋๋ฉ์ด์ ์ด ์ด์ด์ง๋ค.
@Namespace
@Namespace private var animation
- matchedGeometryEffect๊ฐ ์ด๋ค ๋ทฐ๋ค์ด ๊ฐ์ ๋ทฐ์ธ์ง ์ธ์ํ ์ ์๋๋ก ๊ณต๊ฐ(namespace)๋ฅผ ๊ณต์ ํ๋ ๋ณ์
- ๋ทฐ ๊ฐ์ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ
- id: ์ฐ๊ฒฐ์ ์ํ ํค๊ฐ
- animation: ๊ฐ์ ๊ณต๊ฐ ์์ ์๋ค๋ ํ์
ํ๋ก์ ํธ ๋ณ ์ ํ ๊ธฐ์ค
- animation
- ๋จ์ ์ํ ๋ณํ
- withAnimation
- ์ฌ์ฉ์ ์ก์ ์ ๋ฐ๋ผ ๋ช ํํ ์ ์ดํด์ผ ํ ๋
- matchedGeometryEffect + @Namespace
- View ๊ฐ์ ์์ฐ์ค๋ฌ์ด ์ ํ์ด ํ์ํ ๋
์์ ์ฝ๋
//๋ช
์์
struct AnimationView: View {
@Namespace private var animation
@State private var isExpanded = false
var body: some View {
VStack {
if isExpanded {
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.matchedGeometryEffect(id: "card", in: animation)
.frame(width: 300, height: 300)
.onTapGesture {
withAnimation(.spring()) {
isExpanded.toggle()
}
}
} else {
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.matchedGeometryEffect(id: "card", in: animation)
.frame(width: 100, height: 100)
.onTapGesture {
withAnimation(.spring()) {
isExpanded.toggle()
}
}
}
}
}
}
//์๋ฌต์
struct ImplicitAnimationView: View {
@State private var isOn = false
var body: some View {
Circle()
.fill(isOn ? Color.green : Color.red)
.frame(width: isOn ? 200 : 100, height: isOn ? 200 : 100)
.onTapGesture {
isOn.toggle()
}
.animation(.easeInOut, value: isOn)
}
}
3. Transition
- insertion๊ณผ removal์ ์๋ก ๋ค๋ฅธ ํจ๊ณผ ์ ์ฉ ๊ฐ๋ฅ
- ๊ฐ๊ฐ ๋ณ๊ฐ์ identity๋ฅผ ์ ์ฉ ์ํค๊ธฐ์ ๊ฐ๋ฅ
AnyTransition.modifier
- ์ปค์คํ ์ ํ ํจ๊ณผ๋ฅผ ๊ตฌํํ ๋ ์ฌ์ฉํ๋ ๊ณ ๊ธ ๋๊ตฌ
- ๊ธฐ๋ณธ ํจ๊ณผ ์ธ์ ์ง์ ๋ง๋ ์๊ฐ ํจ๊ณผ๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ ์ฉ
- .opacity, .slide, .scale
- ๋ทฐ๊ฐ ์ฝ์ ๋๊ฑฐ๋ ์ ๊ฑฐ๋ ๋, ์ํ๋ ๋ฐฉ์์ผ๋ก ๋ทฐ๋ฅผ ์์ ํด์ ์ ํ ์ ๋๋ฉ์ด์ ์ ๋ง๋ค ์ ์๊ฒ ํด์ฃผ๋ transition ์์ฑ์
AnyTransition.modifier(
active: ViewModifier, // ์ ํ ์ค์ ์ ์ฉ๋ ์ํ
identity: ViewModifier // ํ์ ์ํ
)
์์ ์ฝ๋
//Modifier ์ ์
struct BlurModifier: ViewModifier {
let amount: CGFloat
func body(content: Content) -> some View {
content
.blur(radius: amount)
.opacity(1 - Double(amount / 10))
}
}
//Transition ์ ์
extension AnyTransition {
static var blurFade: AnyTransition {
.modifier(
active: BlurModifier(amount: 10),
identity: BlurModifier(amount: 0)
)
}
}
struct TransitionView: View {
@State private var show = false
var body: some View {
VStack(spacing: 20) {
Button("Toggle Box") {
withAnimation {
show.toggle()
}
}
if show {
RoundedRectangle(cornerRadius: 20)
.fill(Color.purple)
.frame(width: 200, height: 200)
.transition(.blurFade)
}
}
}
}
10th-iOS-Study/์ฌํ/3. ๊ณ ๊ธ ์ ๋๋ฉ์ด์ ์ ํ ๋ฐ ์ ์ค์ณ ํ์ฉ at main · prography/10th-iOS-Study
10๊ธฐ iOS ํํธ์๋ค์ ์คํฐ๋ ๊ณต๊ฐ์ ๋๋ค. Contribute to prography/10th-iOS-Study development by creating an account on GitHub.
github.com
'๐ iOS > ๐ Docs' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS]Unit Test์ ์ฌ์ฉ (0) | 2024.02.05 |
---|---|
[๋์์ธ ํจํด]Observer Pattern (1) | 2024.01.27 |