clyne의 개발 기록
[Swift] 회전 애니메이션 (180도, 90도, 45도, 반시계방향) 본문
안녕하세요.
iOS 개발자 CNOO 입니다.
오늘은 시계방향 및 반시계방향으로 회전하는 애니메이션에 대해서 작성해보려고 합니다.왜냐면...
제가 개발할 때, 반시계방향 회전때문에 고생했던 기억이 있어서....
여러분은 그러지 마시라고 공유드립니다.
먼저, 결과물을 보고 시작하겠습니다.
이미지 뷰 회전 결과
180도 ~ 45도 까지의 버튼을 눌러 한바퀴 도는 예제입니다.
버튼이 눌리는 것을 보여드리기 위해, Ripple효과를 추가했는데, 이것은 이번 포스팅에서 다루지는 않을거예요!
(혹시 원하시는 분 있으시면 리플 효과에 대해서도 포스팅을 작성해보겠습니다 ㅎㅎ)
자, 그럼 코드를 보겠습니다.
1. 뷰 생성 및 배치
(뷰들을 세팅하고, 클릭 이벤트를 넣는 등의 작업에는 RxSwift, RxGesture, Then, Snapkit 모듈을 활용했지만, 굳이 이거 안쓰셔도 됩니다! 그냥 스토리보드에 뷰 그려서 하셔도 돼요! 이번 포스팅의 핵심은 바로 회전이니까요!)
lazy var imageView1 = UIImageView().then {
$0.image = UIImage(systemName: "swift")
$0.tintColor = .black
}
lazy var rotate180Button = UIButton().then {
$0.setTitle("180도", for: .normal)
$0.backgroundColor = .black
}
lazy var rotate180CounterButton = UIButton().then {
$0.setTitle("-180도", for: .normal)
$0.backgroundColor = .brown
}
lazy var rotate90Button = UIButton().then {
$0.setTitle("90도", for: .normal)
$0.backgroundColor = .red
}
lazy var rotate45Button = UIButton().then {
$0.setTitle("45도", for: .normal)
$0.backgroundColor = .blue
}
뷰객체 생성한 후에,
viewDidLoad에 배치하도록 합시다.
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(imageView1)
imageView1.snp.makeConstraints {
$0.center.equalToSuperview()
$0.width.height.equalTo(120)
}
let stackView = UIStackView(arrangedSubviews: [rotate180Button,
rotate180CounterButton,
rotate90Button,
rotate45Button])
stackView.spacing = 5
stackView.distribution = .fillEqually
self.view.addSubview(stackView)
stackView.snp.makeConstraints {
$0.trailing.leading.bottom.equalTo(view.safeAreaLayoutGuide)
$0.height.equalTo(56)
}
// add Gesture
Observable.merge([
rotate180Button.rx.tapGesture().when(.recognized).map{_ in 1},
rotate180CounterButton.rx.tapGesture().when(.recognized).map{_ in 2},
rotate90Button.rx.tapGesture().when(.recognized).map{_ in 3},
rotate45Button.rx.tapGesture().when(.recognized).map{_ in 4},
])
.bind(onNext: rotateImage)
.disposed(by: disposeBag)
}
private func rotateImage(tag: Int){
switch tag {
case 1:
imageView1.rotate180()
case 2:
imageView1.rotate180CounterClock()
case 3:
imageView1.rotate90()
case 4:
imageView1.rotate45()
default:
print("error")
}
}
짜잔~ 뷰 다 그려주고, 클릭했을 때 동작까지 잘 추가해주었습니다.
그러면 이제 , rotateImage 메서드에 있는 rotate함수들에 대해서 보도록 합시다.
2. 이미지 회전 확장함수
이번에도 UIImageView의 확장함수로 만들어서 쓸 거예요.
extension UIImageView {
// 180도 회전
func rotate180(duration: TimeInterval = 0.25){
UIView.animate(withDuration: duration, animations: {
self.transform = self.transform.rotated(by: .pi)
})
}
// 180도 반시계방향 회전
func rotate180CounterClock(duration: TimeInterval = 0.25){
UIView.animate(withDuration: duration, animations: {
self.transform = self.transform.rotated(by: -(.pi*0.999999999999999))
})
}
// 90도 회전
func rotate90(duration: TimeInterval = 0.25){
UIView.animate(withDuration: duration, animations: {
self.transform = self.transform.rotated(by: .pi/2)
})
}
// 45도 회전
func rotate45(duration: TimeInterval = 0.25){
UIView.animate(withDuration: duration, animations: {
self.transform = self.transform.rotated(by: .pi/4)
})
}
}
이렇게 UIImageView 확장함수로 각 회전 방법에 대한 함수를 만들어주시면 됩니다.
그런데, 조금 이상한 코드가 보이지 않나요??
네, 반시계방향을 돌 때, pi값에 (180도) *0.99999... 이 되어있어요.
왜 넣었을까요?
바로, transform함수는 회전 애니메이션을 적용할 때, 더 가까운 쪽으로 회전방향이 결정되게 되어있어요.
즉, 180도 돌라고 시키면 기본값으로 시계방향으로 회전을 하는데요,
-180도를 돌라고 해도, 결국 도착지는 180도의 도착지와 동일하기 때문에 시계방향으로 돕니다.
그래서 180도를 돌리는 것이 아닌, 180도*0.999999 즉, 179.99999 도를 돌게끔 세팅해준 것입니다.
그러면 반대쪽으로 도는 것이 더 가까우니까, 우리의 이미지뷰는 왼쪽으로 돕니다!!
신기하죠?
애플 개발자분들의 섬세함이 돋보이는 대목이네요...
ㅎㅎ
그럼 오늘도 좋은하루 되세요~~
ps. 이걸로 저는 아래쪽에 있는 동영상과 같이 chevron.down 이미지를 돌리는 것을 구현했답니다.
'iOS > Swift' 카테고리의 다른 글
[Swift] Shadow (그림자) 효과 넣기 (0) | 2021.05.04 |
---|---|
[Swift] iOS에서 Visibility GONE 하기 (0) | 2021.05.03 |
[Swift] SubString 편하게 확장해보자! (0) | 2021.05.01 |
[Swift] 특정 모서리만 코너 Round주기 (corner, border) (0) | 2021.04.30 |
[Swift] 간단하게 nil을 필터링! CompactMap! (0) | 2021.04.29 |