Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- map
- SWIFT
- combine
- 리펙토링
- Clean Code
- HIG
- Human interface guide
- Xcode
- 클린 코드
- RxCocoa
- clean architecture
- 리펙터링
- swift documentation
- 애니메이션
- Observable
- 리팩토링
- ios
- uiscrollview
- collectionview
- UICollectionView
- UITextView
- 스위프트
- Refactoring
- Protocol
- ribs
- tableView
- rxswift
- MVVM
- swiftUI
- uitableview
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 콘페티, 폭죽, 눈 효과 (confetti, konfetti, CAEmitterLayer, CAEmitterCell) 본문
iOS 응용 (swift)
[iOS - swift] 콘페티, 폭죽, 눈 효과 (confetti, konfetti, CAEmitterLayer, CAEmitterCell)
jake-kim 2024. 5. 20. 01:53콘페티 효과
- 콘페티 효과를 구현하려면 CAEmitterCell과 CAEmitterLayer를 사용하여 구현
- 폭죽 효과
- 눈 효과
CAEmitterCell, CAEmitterLayer 개념
- CAEmitterCell을 CAEmitterLayer에 추가하고, layer에서 위치를 정한다음 view.layer.addSublayer에 추가하여 구현
- CAEmitterCell로 파티클에 들어갈 하나하나의 요소의 속성값을 정하고, CAEmitterLayer에 이 emitterCell을 주입해주는 것
let emitterCell = (CAEmitterCell 인스턴스)
let emitterLayer = CAEmitterLayer()
emitterLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: UIScreen.main.bounds.height)
emitterLayer.emitterCells = [emitterCell]
view.layer.addSublayer(emitterLayer)
- CAEmitterCell 개념
- cell에는 image, birthRate, velocity, scale등과 같은 속성이 존재
- CAEmitterLayer 개념
- CALayer를 상속받아서 있는 컴포넌트
콘페티 구현 방법
- CAEmitterCell에 콘페티 하나하나의 요소들에 대해 설정값을 정의
- extension으로 정의하고, config를 따로 만들어서 사용하는 곳에서 원하는 값을 넣어서 사용할 수 있도록 구현
// Config는 아래에서 정의
extension CAEmitterCell {
static func item(with config: Config) -> CAEmitterCell {
let cell = CAEmitterCell()
cell.name = config.id
cell.contents = config.image.cgImage
cell.color = config.color?.cgColor
cell.beginTime = CACurrentMediaTime()
cell.duration = config.duration
cell.birthRate = config.birthRate
cell.lifetime = config.lifeTime
cell.velocity = config.velocity
cell.velocityRange = config.velocityRange
cell.emissionRange = .pi * config.emissionRange
cell.emissionLongitude = config.emissionLongitude
cell.scale = config.scale
cell.scaleRange = config.scaleRange
cell.scaleSpeed = 0
cell.spin = config.spinVelocity
cell.spinRange = config.spinRange
cell.yAcceleration = config.accelerationOfGravity
cell.setValue("plane", forKey: "particleType")
cell.setValue(config.spinOrientationRange, forKey: "orientationRange")
cell.setValue(config.spinOrientationLongitude, forKey: "orientationLongitude")
cell.setValue(config.spinOrientationLatitude, forKey: "orientationLatitude")
return cell
}
}
- Config는 아래처럼 preset도 가지고 있는 형태로 구현
struct Config {
enum PresetType {
case firework
case snow
var value: Config {
switch self {
case .firework:
.init(
id: "firework",
image: UIImage(named: "firework")!,
color: UIColor(
white: 1,
alpha: 0.8
),
duration: 2.5,
birthRate: 50,
lifeTime: 10.5,
velocity: 500,
velocityRange: 50,
emissionRange: 0.25,
emissionLongitude: 0,
scale: 0.01,
scaleRange: 0.2,
spinVelocity: 1,
spinRange: 0.5,
accelerationOfGravity: 200,
spinOrientationRange: .pi,
spinOrientationLongitude: .pi,
spinOrientationLatitude: 0
)
case .snow:
.init(
id: "snowfall",
image: UIImage(named: "firework")!,
color: UIColor(
white: 1,
alpha: 0.8
),
duration: 10,
birthRate: 10,
lifeTime: 60,
velocity: 50,
velocityRange: 20,
emissionRange: .pi,
emissionLongitude: 0,
scale: 0.1,
scaleRange: 0.05,
spinVelocity: 0,
spinRange: 0,
accelerationOfGravity: 200,
spinOrientationRange: 0,
spinOrientationLongitude: 0,
spinOrientationLatitude: 0
)
}
}
}
/// 셀의 고유 식별자
let id: String
/// 셀에 표시되는 이미지
let image: UIImage
/// 셀의 색상
let color: UIColor?
/// 셀의 지속 시간: emitter의 활성화된 시간을 결정
let duration: TimeInterval
/// 초당 생성량: 초당 생성되는 emitter의 수 비율을 결정
let birthRate: Float
/// 셀의 수명: emitter 각 하나가 살아있는 시간(초)
let lifeTime: Float
/// emitter의 속도: 이게 클수록 높이 튀어오름
let velocity: CGFloat
/// 속도 범위: emitter의 초기 속도에 대한 무작위성을 결정
let velocityRange: CGFloat
/// emitter 방출 각도 범위: emitter가 방출되는 각도의 범위를 결정
let emissionRange: CGFloat
/// emitter 방출 각도: emitter가 방출되는 방향을 결정
let emissionLongitude: CGFloat
/// 크기 비율: emitter의 크기를 결정
let scale: CGFloat
/// 크기 범위: emitter의 크기에 대한 무작위성을 결정
let scaleRange: CGFloat
/// 회전 속도: emitter의 회전 속도를 결정
let spinVelocity: CGFloat
/// 회전 범위: emitter의 회전 속도에 대한 무작위성을 결정
let spinRange: CGFloat
/// 중력 가속도: 중력 가속도를 설정 (양수 값은 아래로 향하는 중력을 표현)
let accelerationOfGravity: CGFloat
/// 회전 방향 범위: emitter의 회전 방향의 범위를 결정
let spinOrientationRange: CGFloat
/// 회전 방향 경도: emitter의 회전 방향의 경도를 결정
let spinOrientationLongitude: CGFloat
/// 회전 방향 위도: emitter의 회전 방향의 위도를 결정
let spinOrientationLatitude: CGFloat
}
사용하는 쪽)
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// showFirework()
showSnowfall()
}
func showFirework() {
let emitterCell = CAEmitterCell.item(with: Config.PresetType.firework.value)
let emitterLayer = CAEmitterLayer()
emitterLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: UIScreen.main.bounds.height)
emitterLayer.emitterShape = .line
emitterLayer.emitterSize = CGSize(width: view.bounds.width, height: 1)
emitterLayer.emitterCells = [emitterCell]
view.layer.addSublayer(emitterLayer)
}
func showSnowfall() {
let emitterCell = CAEmitterCell.item(with: Config.PresetType.snow.value)
let emitterLayer = CAEmitterLayer()
emitterLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 100)
emitterLayer.emitterShape = .line
emitterLayer.emitterSize = CGSize(width: view.bounds.width, height: 1)
emitterLayer.emitterCells = [emitterCell]
view.layer.addSublayer(emitterLayer)
}
}
(완성)
콘페티 구현 시 주의할 점
- 로컬에 있는 이미지를 사용할 경우, 디바이스의 스케일(UIScreen.main.scale)값에 따라서 크기가 다 다르게 나오므로 주의할 것
- ex) UIImage의 인스턴스에서 size를 찍어봤을때 60*60의 로컬 이미지 scale을 0.1로 설정했을 때, x3 디바이스에서는 180*180으로 설정되므로 각 180 * 0.1 길이만큼 적용되고, x2인 디바이스에서는 120 * 0.1만큼 사이즈가 적용됨
- 이 사이즈는 이미지 본연의 사이즈에 적용되므로 디바이스마다 크기가 다르게나옴
- 때문에 디바이스별로 scale을 다르게 가져갈 것
* 전체 코드: https://github.com/JK0369/ExConfetti
* 참고
- https://stackoverflow.com/questions/43672353/swift-3-rotate-map-with-user-direction
- https://nsios.tistory.com/53
- https://developer.apple.com/documentation/quartzcore/caemitterlayer
- https://developer.apple.com/documentation/quartzcore/caemittercell
'iOS 응용 (swift)' 카테고리의 다른 글
Comments