순진이의 하루/study정리
8. 고차함수, UIViewPropertyAnimator_2022.08.29
순진이
2022. 8. 29. 09:32
고차함수
var numbers = Array(1...100)
1. ForEach
// 1. ForEach
numbers.forEach { number in
print(number)
}
// 단축문법
numbers.forEach { print($0) }
-for문으로도 가능
var doubledNumbers = [Int]()
for number in numbers {
let doubleNumber = number * 2
doubledNumbers.append(doubleNumber)
}
2. map
// 2. map
numbers.map { number in
return number * 2
}
// 단축문법
numbers.map { $0 * 2 }
-for문으로도 가능
for number in numbers {
doubledNumbers.append(number)
}
3. filter
// 3. filter
numbers.filter { number in
number > 0
}
// 단축 문법
numbers.filter { $0 > 0 }
-for문으로도 가능
var higherThanFifty = [Int]()
for number in numbers where number > 50 {
higherThanFifty.append(number)
}
4. reduce
// 4. reduce
numbers.reduce(0) { partialResult, number in
partialResult + number
}
// 단축문법
numbers.reduce(0) { $0 + $1 }
// 0 { 0, 0 in 0 + 1 } => 1
// 1 { 1, 1 in 1 + 1 } => 2
-for문으로도 가능
var sum = 0
for number in numbers {
sum += number
}
Animation
1. UIView.animate
import UIKit
class ViewController: UIViewController {
let redView = UIView()
let button = UIButton(type: .system)
var topAnchor: NSLayoutConstraint?
var leadingAnchor: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
@objc func buttonTapped(_ sender: UIButton) {
animate()
}
private func animate() {
UIView.animate(withDuration: 1.0, delay: 0, options: [], animations: {
self.leadingAnchor?.constant = self.view.frame.width - 80
// 레드뷰의 넓이가 60이니까 80만큼 -하면 끝이 20만큼 남을 거임
self.view.layoutIfNeeded()
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0, options: [], animations: {
self.topAnchor?.constant = self.view.frame.height - 140
self.view.layoutIfNeeded()
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0, options: [], animations: {
self.leadingAnchor?.constant = 20
self.view.layoutIfNeeded()
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0, options: [], animations: {
self.topAnchor?.constant = 80
self.view.layoutIfNeeded()
}, completion: { _ in })
})
})
})
}
}
//MARK: -UI
extension ViewController {
final private func configureUI() {
setAttributes()
addTarget()
setConstraints()
}
final private func setAttributes() {
redView.backgroundColor = .red
button.setTitle("button", for: .normal)
}
final private func addTarget() {
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
final private func setConstraints() {
[redView, button].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
topAnchor = redView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80)
topAnchor?.isActive = true
leadingAnchor = redView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20)
leadingAnchor?.isActive = true
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50),
redView.widthAnchor.constraint(equalToConstant: 60),
redView.heightAnchor.constraint(equalToConstant: 60)
])
}
}
callback 지옥에 갇힘
2. keyFrame
private func animateKeyFrame() {
UIView.animateKeyframes(withDuration: 4, delay: 0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.25, animations: {
self.leadingAnchor?.constant = self.view.frame.width - 80
// 상대적인(relative) startTime임! : 0.0 ~ 1.0 까지만 존재함
// relativeDuration : 전체 시간 중에 duration. 여기서는 4초에서 0.25니까 1초임
self.view.layoutIfNeeded()
})
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25) {
self.topAnchor?.constant = self.view.frame.height - 140
self.view.layoutIfNeeded()
}
UIView.addKeyframe(withRelativeStartTime: 0.50, relativeDuration: 0.25) {
self.leadingAnchor?.constant = 20
self.view.layoutIfNeeded()
}
UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) {
self.topAnchor?.constant = 80
self.view.layoutIfNeeded()
}
})
}
3. UIViewPropertyAnimator
import UIKit
class ViewController: UIViewController {
enum Button: String, CaseIterable {
case start
case pause
case stop
}
private let redView = UIView()
private var stackView = UIStackView()
private var animator: UIViewPropertyAnimator?
private var topAnchor: NSLayoutConstraint?
private var leadingAnchor: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
@objc func buttonTapped(_ sender: UIButton) {
switch sender.currentTitle {
case Button.start.rawValue:
startAnimation()
case Button.pause.rawValue:
pauseAnimation()
case Button.stop.rawValue:
stopAnimation()
default:
fatalError()
}
}
private func startAnimation() {
if animator == nil {
animator = UIViewPropertyAnimator(duration: 4, timingParameters: UICubicTimingParameters())
animator?.addAnimations {
UIView.animateKeyframes(withDuration: 4, delay: 0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/6, animations: {
self.leadingAnchor?.constant = self.view.frame.width - 80
// 상대적인(relative) startTime임! : 0.0 ~ 1.0 까지만 존재함
// relativeDuration : 전체 시간 중에 duration. 여기서는 4초에서 0.25니까 1초임
self.view.layoutIfNeeded()
})
UIView.addKeyframe(withRelativeStartTime: 1/6, relativeDuration: 2/6) {
self.topAnchor?.constant = self.view.frame.height - 140
self.view.layoutIfNeeded()
}
UIView.addKeyframe(withRelativeStartTime: 3/6, relativeDuration: 1/6) {
self.leadingAnchor?.constant = 20
self.view.layoutIfNeeded()
}
UIView.addKeyframe(withRelativeStartTime: 4/6, relativeDuration: 2/6) {
self.topAnchor?.constant = 80
self.view.layoutIfNeeded()
}
})
}
}
animator?.startAnimation()
}
private func pauseAnimation() {
// 잠시 멈춤. 다시 시작할 수 있음
animator?.pauseAnimation()
}
private func stopAnimation() {
// 애니메이션 완전 끝
animator?.stopAnimation(false)
animator?.finishAnimation(at: .current)
animator = nil
topAnchor?.constant = 80
leadingAnchor?.constant = 20
view.setNeedsLayout()
}
}
//MARK: -UI
extension ViewController {
final private func configureUI() {
setAttributes()
setConstraints()
}
final private func setAttributes() {
redView.backgroundColor = .red
}
final private func setConstraints() {
stackView.alignment = .fill
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
[redView, stackView].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
Button.allCases.forEach {
let button = UIButton(type: .system)
stackView.addArrangedSubview(button)
button.setTitle($0.rawValue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
topAnchor = redView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80)
topAnchor?.isActive = true
leadingAnchor = redView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20)
leadingAnchor?.isActive = true
NSLayoutConstraint.activate([
redView.widthAnchor.constraint(equalToConstant: 60),
redView.heightAnchor.constraint(equalToConstant: 60),
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60),
stackView.widthAnchor.constraint(equalToConstant: view.frame.width - 40)
])
}
}
UIViewPropertyAnimator class는 UIViewAnimating이라는 프로토콜을 채택하고 있기 때문에 starting, stopping, modifying할 수가 있다.