일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 데이터최적화
- combine
- UIKit
- WWDC
- authentication
- realitykit
- RxSwift
- arkit
- withAnimation
- Network
- state
- gesture
- CS
- dataflow
- Performance
- Concurrency
- fullscreencover
- SwiftUI
- GCD
- swift
- 네트워크
- environmentobjet
- ios
- stateobject
- iphone
- 달력
- Animation
- ar
- auth
- firebase
- Today
- Total
XLOG
[SwiftUI] Camera Shutter Button Animation 본문
저번 Animation 공부한 내용을 바탕으로 Camera Shutter 를 구현해 보았다.
우선 카메라 셔터 부터 시작을 해보자.
struct ContentView: View {
@State var buttonTapped: Bool = false
var body: some View {
ZStack {
Color.black
ZStack {
// 외각 테두리
Circle()
.stroke(lineWidth: 4)
.foregroundColor(.white)
// 내부 원
Circle()
.foregroundColor(buttonColor)
.frame(width: buttonTapped ? 56 : 60, height: buttonTapped ? 56 : 60)
}
.frame(width: 72, height: 72)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)){
buttonTapped.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
withAnimation(.easeInOut(duration: 0.3)){
buttonTapped.toggle()
}
}
}
}
}
}
UIKit 이 었다면 UIView.animation 의 CompletionHandler 를 통해 다시 되돌아가도록 구현을 했겠지만, SwiftUI 의 withAnimation 에는 completion 이 없는 것 같아 DispatchQueue.main.asyncAfter 를 이용하여 구현하였다.
사실 여기까지는 간단하다.
하지만 조금 고생한 부분은 영상촬영 시작에 대한 문제였다. UIKit 에선 LongPressGestureRecognizer 를 사용하면 제스쳐의 시작과 끝을 알 수 있었다. 하지만 SwiftUI 의 LongPressGesture의 경우 minimumDuration 을 넘기게 되면 gesture의 onEnded가 호출이 되었다. 그래서 여기서 편법을 사용하였다. 그건 바로 DragGesture이다.
DragGesture 는 Gesture 인식이 시작될 때 onChaged 가 호출이 된다. 하지만 주의할 점은 손을 움직일 때마다 onChanged 가 호출이 되기 때문에 그 동작을 막아줘야 한다.
struct ContentView: View {
@State var buttonTapped: Bool = false
@State var buttonColor: Color = .white
@State var scale: CGFloat = 1
var body: some View {
ZStack{
Color.black
ZStack {
Circle()
.stroke(lineWidth: 4)
.foregroundColor(.white)
Circle()
.foregroundColor(buttonColor)
.frame(width: buttonTapped ? 56 : 60, height: buttonTapped ? 56 : 60)
.scaleEffect(scale)
}
.frame(width: 72, height: 72)
.onTapGesture {
''''''
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
//살짝 확대될 때의 Scale 값
let tempScale: CGFloat = 75 / 60
// 애니메이션이 시작하고 진행중인 경우 scale 이 1 인 상황은 처음밖에 없다
// 그렇기에 아무리 제스쳐를 움직여도 한번만 애니메이션이 실행되게 된다.
if scale == 1 {
withAnimation(.easeInOut(duration: 0.3)) {
buttonTapped.toggle()
buttonColor = .red
scale = tempScale
}
}
// withAnimation으로 scale을 tempScale을 바꿔줬을때 duration 뒤에 scale 이 바뀐다는 의미가 아니다.
// duraction 을 주더라도 scale 은 즉시 변경이므로 작아지는 애니메이션 또한 한번만 실행
if scale == tempScale {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
withAnimation(.easeInOut(duration: 0.3)) {
scale = 50 / 60
}
}
}
})
.onEnded({ value in
withAnimation(.spring()) {
withAnimation(.spring(response: 0.2)) {
buttonColor = .white
scale = 1
}
}
})
)
}
}
}
근데 만들고 나서 기본 카메라 어플을 보니....한번 작아졌다 확대되고 다시 작아진다.....
이걸 구현하기 위해선 UIKit의 TouchesBegan, TapGesutre, LongPressGesutre 를 사용하면 쉽게 될 것으로 보인다.
하지만....SwiftUI 는 바로 떠오르지 않는다....아직 Animation에 대한 공부가 더 필요할 것 같다.
내일이면 새로운 Swift 버전이 풀린다. 그렇게 되면 keyframe을 사용할 수 있으니 DispatchQueue 같은 GCD 를 사용할 필요도 없어질 것 같은데.... 우선 다음에 더 가다듬어 업로드를 해봐야 겠다.
전체코드
Github: https://github.com/profit0124/SwiftUICameraShutterAnimation
'Swift > SwiftUI' 카테고리의 다른 글
[SwiftUI] Button 의 highlighted 감지하기 (0) | 2023.12.01 |
---|---|
[SwiftUI] Infinity Carousel 구현 (0) | 2023.12.01 |
[SwiftUI] Animation 적용 기본 이론편 (0) | 2023.08.07 |
[SwiftUI] QR Code Scanner 만들기 (SwifUI 에 ViewController 사용하기) (1) | 2023.03.08 |
[SwiftUI] 날짜 계산, 월간달력 만들기 (0) | 2023.03.04 |