Sin Lin
16 min readJan 11, 2023

#15 Random亂數練習|除了手動,我們也可以交給命運來安排。

這次要挑戰的作業是下面這個👇主題決定蹭一下聯名的熱潮😆

聯名的商業模式除了在大街小巷穿梭以外(五桐號和Dinotaeng的聯名好可愛😍),最近幾年精品界也愛上這種A+B蹦出新滋味的玩法,Nike X Dior、Gucci X 哆啦A夢還有今年初的Louis Vuitton X 草間彌生,聯名風潮看來還會吹很~久。
這次的作業想讓Dior和Louis Vuitton做一次假聯名😛,來玩客製化包包遊戲!

📍作業目標:
👉使用Slider修改畫面上指定物件的顏色
👉使用亂數功能隨機產生顏色
👉使用Switch來控制畫面動畫

✨先上作品!👀

歡迎廠商找我合作😆

1.先將素材匯入Assets,並將Storyboard的元件都拉好。這邊要注意SliderLabel的文字框要能裝得下最大值255,可以先設255確認數字不會超出去,不然就會像下面這樣產生悲劇。

2.判斷畫面上的元件哪些要拉Action與Outlet,先思考物件之間的控制關係。

・圖片:顏色被Slider改變
・Label:數字被Slider改變
・Slider:獲得使用者的數值+控制Label與圖片
・Switch:獲得使用者是否開/關按鈕+控制星星出現
・Button:產生亂數改變Slider數值進而改變圖片顏色

3.開始寫程式~仔細看發現底部的LV Pattern與包包的角度不合,所以要先旋轉ImageView。先將LV Pattern的圖片拉一個Outlet,才能在程式碼裡面命令他工作,然後再將之前學過的旋轉ImageView程式碼加進去。

//此行寫在viewDidLoad fuction內,因為希望圖便在一打開畫面時就要旋轉完畢
let oneDegree = CGFloat.pi / 180
louisVuittonImageView.transform = CGAffineTransform.identity.rotated(by: oneDegree * -30)
一張為修改前,一張為修改後

4.Slider設定。拉動slider時,會有兩個動作:圖片改顏色以及slider label數字改變。

💡Tips1:像Slider這樣的元件,可以同時拉Action和Outlet。(做動和回饋都是同一個元件)
💡Tips2:如果不同元件都是一樣的Action,可以只拉一次就好。

4–1:設定圖片與Slider之間的關係。因為我們要改的是LV的背景,所以是設定backgroundColor去存入顏色資料。

 //這邊寫在@IBAction func changeColor內
//因為RGB的值最多為1,所以要/255
louisVuittonImageView.backgroundColor = UIColor(red: CGFloat(redSlider.value/255), green: CGFloat(greenSlider.value/255), blue:CGFloat(blueSlider.value/255), alpha: 1)

4–2:設定Label與Slider之間的關係。在設定時,發現SliderLabel會是浮點數的方式呈現,這是因為value的型別是Float,所以要顯辦法把它轉換為整數,這邊列出三種不同的寫法,都可以達到同樣的效果。

//這邊寫在@IBAction func changeColor內

//Slider後的數字設定
redLabel.text = String(Int(redSlider.value))
greenLabel.text = Int(greenSlider.value).description
blueLabel.text = String(format:"%.0f", blueSlider.value)
//"%.0f"是要取到小數點後幾位的寫法,這邊寫取到第0位代表整數

🚨Storyboard上的Attributes Inspector Value預設值只有針對Slider物件本身,Label並不會改變,所以我們要自行為Label加入預設值。

//此行寫在viewDidLoad fuction內,因為希望畫面一開啟就是顯示255
redLabel.text = "255"
greenLabel.text = "255"
blueLabel.text = "255"
Storyboard Slider的設定預設Value為255。中間圖片的Slider Thumb明明在中間,後面的數字卻是0。最後的圖為設定預設值後。

5.亂數Random Button設定,寫在亂數按鈕的Action裡。

 //設定亂數,因為這個按鈕是一次產生3個針對Slider的亂數,所以3個slider都寫在此Func內
redSlider.value = Float.random(in: 0...255)
greenSlider.value = Float.random(in: 0...255)
blueSlider.value = Float.random(in: 0...255)

因為剛剛changeColor的功能是寫在別的IBAction Function裡,所以當亂數按鈕的Function也需要一樣功能時並不能直接呼叫(會連Function內的其他功能一起呼叫),要再寫一次(複製貼上就好啦~)。

 //複製剛剛寫好的changeColor程式碼
//Image
louisVuittonImageView.backgroundColor = UIColor(red: CGFloat(redSlider.value/255), green: CGFloat(greenSlider.value/255), blue: CGFloat(blueSlider.value/255), alpha: 1)
//Slider
redLabel.text = String(Int(redSlider.value))
greenLabel.text = Int(greenSlider.value).description
blueLabel.text = String(format:"%.0f", blueSlider.value)

6.最後是參考同學作品,想要練習Switch的功能,所以我加入了星星動畫✨順便複習之前學到頭很痛的動畫設定😂

💡Tips1:因為在開啟畫面與Switch控制時都需要這個功能,所以將動畫做成Function,之後要使用再呼叫Function即可。
💡Tips2:做動畫的時候會有兩個常數:發射器細胞(Cell)與圖層(Layer),要將Layer寫在Function外,寫在Function內的話,每一次呼叫Function都會新增一個Layer,到時候只能唱星星堆滿天~

//常數會寫在Class裡面、所有Function外面
let starLayer = CAEmitterLayer()
func starEmitter(){
//設定星星動畫
let starEmitterCell = CAEmitterCell()
starEmitterCell.contents = UIImage(named: "star")?.cgImage

starEmitterCell.birthRate = 0.5
starEmitterCell.lifetime = 100
starEmitterCell.velocity = 1
starEmitterCell.yAcceleration = 1
starEmitterCell.scale = 0.3
starEmitterCell.spin = 0.5
starEmitterCell.emissionRange = .pi*0.5

//將 let starLayer = CAEmitterLayer()寫在func外
starLayer.emitterCells = [starEmitterCell]
starLayer.emitterPosition = CGPoint(x: view.bounds.width/2, y: -50)
starLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
starLayer.emitterShape = .line
view.layer.addSublayer(starLayer)

}
如果將Layer常數寫在Function內星星就會越來越多...

7.Switch設定,因為只有開和關兩種模式,所以可以用if-else去寫。讓星星消失其實只是把圖層從Superlayer中移除,但動畫在程式中還在跑,仔細看Gif會發現關掉再開啟後星星還是延續關掉前的樣子,只是往下移動了一點。

    @IBAction func starSwitch(_ sender: Any) {
if starEmitterControl.isOn{
starEmitter()
} else {
starLayer.removeFromSuperlayer()
}
}

8.美化一下介面細節就完成啦!🥹

Done!

9.上傳GitHub

📍完整程式碼

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var louisVuittonImageView: UIImageView!
@IBOutlet weak var redSlider: UISlider!
@IBOutlet weak var greenSlider: UISlider!
@IBOutlet weak var blueSlider: UISlider!
@IBOutlet weak var redLabel: UILabel!
@IBOutlet weak var greenLabel: UILabel!
@IBOutlet weak var blueLabel: UILabel!
@IBOutlet weak var starEmitterControl: UISwitch!
let starLayer = CAEmitterLayer()


override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//旋轉LV的圖片
let oneDegree = CGFloat.pi / 180
louisVuittonImageView.transform = CGAffineTransform.identity.rotated(by: oneDegree * -30)
//設定SliderLabel的預設值
redLabel.text = "255"
greenLabel.text = "255"
blueLabel.text = "255"
//一開畫面就有星星
starEmitter()


}
@IBAction func changeColor(_ sender: Any) {
//三個slider功能是一樣的,可以拉到同一個Action,就不用寫三個Action的內容
//拉動slider時,會有兩個動作,圖片改顏色以及slider label數字改變

//因為RGB的值最多為1,所以要/255
louisVuittonImageView.backgroundColor = UIColor(red: CGFloat(redSlider.value/255), green: CGFloat(greenSlider.value/255), blue: CGFloat(blueSlider.value/255), alpha: 1)
//Slider後的數字設定
redLabel.text = String(Int(redSlider.value))
greenLabel.text = Int(greenSlider.value).description
blueLabel.text = String(format:"%.0f", blueSlider.value)
//"%.0f"是要取到小數點後幾位的寫法,這邊寫取到第0位代表整數


}

@IBAction func random(_ sender: Any) {
//設定亂數,因為這個按鈕是一次產生3個針對Slider的亂數,所以3個slider都寫在此Func內
redSlider.value = Float.random(in: 0...255)
greenSlider.value = Float.random(in: 0...255)
blueSlider.value = Float.random(in: 0...255)

//複製剛剛寫好的changeColor程式碼
//Image
louisVuittonImageView.backgroundColor = UIColor(red: CGFloat(redSlider.value/255), green: CGFloat(greenSlider.value/255), blue: CGFloat(blueSlider.value/255), alpha: 1)
//Slider
redLabel.text = String(Int(redSlider.value))
greenLabel.text = Int(greenSlider.value).description
blueLabel.text = String(format:"%.0f", blueSlider.value)
}
func starEmitter(){
//設定星星動畫
let starEmitterCell = CAEmitterCell()
starEmitterCell.contents = UIImage(named: "star")?.cgImage

starEmitterCell.birthRate = 0.5
starEmitterCell.lifetime = 100
starEmitterCell.velocity = 1
starEmitterCell.yAcceleration = 1
starEmitterCell.scale = 0.3
starEmitterCell.spin = 0.5
starEmitterCell.emissionRange = .pi*0.5

//將 let starLayer = CAEmitterLayer()寫在func外
starLayer.emitterCells = [starEmitterCell]
starLayer.emitterPosition = CGPoint(x: view.bounds.width/2, y: -50)
starLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
starLayer.emitterShape = .line
view.layer.addSublayer(starLayer)

}

@IBAction func starSwitch(_ sender: Any) {
if starEmitterControl.isOn{
starEmitter()
} else {
starLayer.removeFromSuperlayer()
}
}

}

💡參考的作品