# 15 利用 viewDidLoad 客製 App 畫面

***
彼得潘的 Swift iOS / Flutter App 開發教室
11 min readMay 14, 2022

練習目的:在viewDidLaod中用程式做出想要的App畫面

2022 / 05 / 05 為 CHANEL 的 Cruise 2022/23 渡假系列時尚大秀,第一眼看到 CHANEL 最新發布的 2023 早春大秀前導影片就被深深的吸引住了,因此就來以這個為主題吧!

https://youtube.com/shorts/-poGlmJq3VA?feature=share

使用到的功能:

  • 利用 AVPlayerLooper 重覆播放音樂和影片
  • 利用 Mask 產生遮罩
  • 利用 CAEmitterLayer 製造發射粒子效果
  • 利用 if else 讓按鈕判斷操作

利用 AVPlayerLooper 重覆播放音樂和影片

加入音樂需先 import AVFoundation

import AVFoundation

為了讓音樂能不間斷播放,需在 viewDidLoad 之前宣告 AVPlayerLooper 以讓音樂重複播放,如果在 viewDidLoad 裡面宣告,當 viewDidLoad 內的程式跑完後,player 就會被清掉造成音樂無法再次播放

var looper: AVPlayerLooper?

在 viewDidLoad 內加入播放音樂的程式:

override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.
if let url = URL(string: "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/b4/d3/ea/b4d3ea48-71c5-c3db-ea6a-02c134cc5067/mzaf_9371911637032292459.plus.aac.p.m4a"){let player = AVQueuePlayer()let item = AVPlayerItem(url: url)looper = AVPlayerLooper(player: player, templateItem: item)player.play()}

我放的是 TLC 的 Baby Baby Baby,真是老靈魂~

參考文章:

利用 Mask 產生遮罩

let backgroundImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 375, height: 812))backgroundImage.image = UIImage(named: "Beach")backgroundImage.contentMode = .scaleAspectFill//在做Mask時,x、y都要設為0,代表他會與要重疊的圖對齊let chanelImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 375, height: 83))chanelImage.image = UIImage(named: "CHANEL")chanelImage.contentMode = .scaleAspectFill//在做Mask時,x、y都要設為0,代表他會與要重疊的圖對齊let pinkImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 375, height: 83))pinkImage.backgroundColor = UIColor(red: 255/255, green: 20/255, blue: 174/255, alpha: 0.9)pinkImage.mask = chanelImage//完成產生Mask後,再去設定綁放位置pinkImage.frame = CGRect(x: 0, y: 354.5, width: 375, height: 83)//insertSubView用來控制圖片添加到哪一層,因為background是要放在最底層,因此設定at: 0view.insertSubview(backgroundImage, at: 0)//addSubview用來將圖片一層一層往上加view.addSubview(pinkImage)

利用 CAEmitterLayer 製造發射粒子效果

製作發射 Date 的動畫

func dateEmitter(){//產生發射器let dateEmitterCell = CAEmitterCell()//設定要發射的內容dateEmitterCell.contents = UIImage(named: "5-5-22")?.cgImage//設定每秒產生多少datedateEmitterCell.birthRate = 6//設定date在畫面的停留時間dateEmitterCell.lifetime = 20//設定date移動的初始速度dateEmitterCell.velocity = 10//設定date移動速度的可變化量dateEmitterCell.velocityRange = 10//設定date下降的加速度dateEmitterCell.yAcceleration = 10dateEmitterCell.xAcceleration = 0//設定date的大小比例dateEmitterCell.scale = 1.4//設定date的大小比例可變化量dateEmitterCell.scaleRange = 1//設定date的大小在畫面中時的變化速度dateEmitterCell.scaleSpeed = -0.02//設定date的旋轉速度dateEmitterCell.spin = 0.5//設定date的旋轉速度可變化量dateEmitterCell.spinRange = 0.5//設定發射的角度dateEmitterCell.emissionRange = .pi*0.5//設定圖層上的內容dateEmitterLayer.emitterCells = [dateEmitterCell]//設定發射的中心位置dateEmitterLayer.emitterPosition = CGPoint(x: view.bounds.width/2, y: -100)//設定發射的形狀大小dateEmitterLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)//設定發射的形狀dateEmitterLayer.emitterShape = .lineview.layer.addSublayer(dateEmitterLayer)}

製作發射 Location 的動畫

func locationEmitter(){let locationEmiterCell = CAEmitterCell()locationEmiterCell.contents = UIImage(named: "Monte-Carlo Monaco")?.cgImagelocationEmiterCell.birthRate = 5locationEmiterCell.lifetime = 20locationEmiterCell.velocity = 20locationEmiterCell.velocityRange = 10locationEmiterCell.yAcceleration = 10locationEmiterCell.xAcceleration = 0locationEmiterCell.scale = 1.3locationEmiterCell.scaleRange = 0.5locationEmiterCell.scaleSpeed = -0.02locationEmiterCell.spin = 0.5locationEmiterCell.spinRange = 0.5locationEmiterCell.emissionRange = .pilocationEmitterLayer.emitterCells = [locationEmiterCell]locationEmitterLayer.emitterPosition = CGPoint(x: view.bounds.width/2, y: -150)locationEmitterLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)locationEmitterLayer.emitterShape = .lineview.layer.addSublayer(locationEmitterLayer)}

參考文章:

利用 if else 讓按鈕判斷操作

因為等等要在按鈕上作 if else 的判斷,我要判斷的是當 particle 為不同參數的時候,按鈕應該發射不同的粒子,因此須在 viewDidLoad 前先宣告 particle 的初始值

var particle = "date"

因為要控制 particle 不同參數發射不同的粒子,也就是說須做到當參數為 A 即發射 A 粒子,同時暫停 B 粒子發射,反之亦然。因此 A、B 發射粒子圖層只需要在 Controller 內各生成一個 CAEmitterLayer(),來控制他是否顯示圖層,不用每次都重新生成一次 CAEmitterLayer(如果把 CAEmitterLayer() 生成在 function 內,則每次呼叫 function 時都會重新生成 CAEmitterLayer,當然 CAEmitterLayer 的樣式設定還是可以寫在 function 中)

let dateEmitterLayer = CAEmitterLayer()let locationEmitterLayer = CAEmitterLayer()

串連Button設定觸發後的行為

當 particle 為 Date 時,呼叫 dateEmitter() 發射 Date 的動畫,並且移除 Location 粒子圖層,particle 變為 Location ; 當 particle 為 Location 時,呼叫 locationEmitter() 發射 Date 的動畫,並且移除 Date 粒子圖層,particle 變為 Date

@IBAction func changeEmitter(_ sender: Any) {if particle == "date"{dateEmitter()locationEmitterLayer.removeFromSuperlayer()particle = "location"}else{locationEmitter()dateEmitterLayer.removeFromSuperlayer()particle = "date"}}

--

--