#18 調色大師 - Swimming Deer

mask(遮罩),Segmented Control,Switch,Slider,Button

作品截圖:

運用到的 UIKit 有:

🌟 View
🌟 Image View
🌟 Slider
🌟 Label
🌟 Switch
🌟 Segmented Control
🌟 Button

其他技術:

🌟 mask 遮罩
🌟 IBOutlet
🌟 IBAction

APP 功能說明:

🌟 鹿角跟頭分別對應 Segmented Control Index 的 0 與 1 ,能夠切換用 Slider各自變色

🌟 Random 與 Reset Button 能夠各自分別應用在角與頭

🌟 Random 的 alpha value為 0.5~1,其餘紅綠黃數值為 0~255

🌟 紅綠藍及透明度的 Switch 都要 On,Random 與 Reset Button才能作用

🌟 Switch 的 On / Off 則同時反映在角與頭,切到 Off 時,對應的紅色、綠色及藍色數值要能歸 0 且不能再作用,影像也要正確反應顏色變化,其他 On 的 Switch 繼續可以調控顏色,唯獨 Alpha 值 Switch Off 後要等於 1(0是全透明,就什麼都看不到了)

🌟 各個 Label 要正確顯示對應的 Slider Value

🌟 下方三組 Slider 能調控圓角幅度、邊框厚度及陰影大小,一樣一旦 Switch Off 則對應的 Slider 就不能作用且影像失去效果

心得:

這是花了最久的一次去完成一個作業,也是目前收穫最多的一次。

發現除了運用 Peter 上課及教材等給 coding 訊息與觀念外,還需具備柯南般的觀察力,去看懂及分辨前輩們寫的作業(正確或是多餘的程式碼),之後才有可能內化成自己的能力去活用這些技術。

遇到的問題:

讓我卡關最久的部分,是在 RGBA 顏色的 Switch 設定,在 Switch On 時讓Slider 調控沒有問題;最大問題是在於 Switch Off 時,要讓 Slider Value 歸 0,Label要正確顯示外,鹿角及頭部的影像要正確顯示/反應顏色變化 (除了alpha 值要等於1)。起初好像很快地就寫出程式碼,沒出現紅字還開心了一下,但下一秒開 Simulator 就悲劇了,發生過的悲劇有:其他 Slider 跟著變動、Label數值不正確顯示、影像數值沒有真正歸零、同樣的 Switch Off 在 Segmented Control Index 0與1 結果不同等等。

解決的過程:

要清楚認識每一條程式碼的作用是什麼,有時可以試著刪除一句程式碼,然後開 Simulator 觀察有什麼變化。數次的調整後,現在有稍稍看似達成目標,但還是有點小瑕疵,例如:在 Index 0 關掉任一個 Switch,照理在 Index 1 的影像也要同步改變,但現在的狀態是要切換到 Index 1 時影像才會改變。

Recording GIF:

GIF

設定 function:

參考了前輩們的作品,看懂設定 function 的用意後,可使思路更清楚一些,可參考這樣的做法,給自己一個在打程式碼的大方向的概念

//讓 Label 顯示對應的 Slider value
func colorValueLabel() {

redValueLabel.text = String(Int(redSlider.value*255))
greenValueLabel.text = String(Int(greenSlider.value*255))
//整數也可以這樣寫
blueValueLabel.text = String(format:"%.0f", blueSlider.value*255)
alphaValueLabel.text = String(format: "%.1f", alphaSlider.value)
}
// 每次移動 Slider 後將對應的value儲存至原本變數中(slider數值存入顏色數值)
func hornsColorValueSave(){

hornsRedValue = redSlider.value
hornsGreenValue = greenSlider.value
hornsBlueValue = blueSlider.value
hornsAlphaValue = alphaSlider.value
}

func headColorValueSave(){

headRedValue = redSlider.value
headGreenValue = greenSlider.value
headBlueValue = blueSlider.value
headAlphaValue = alphaSlider.value
}

//用已儲存進變數的Value(新的顏色數值)讓Slider跳至相對應的位置(顏色數值存入slider數值)
func hornsColorSliderSet(){

redSlider.setValue(hornsRedValue, animated: true)
greenSlider.setValue(hornsGreenValue, animated: true)
blueSlider.setValue(hornsBlueValue, animated: true)
alphaSlider.setValue(hornsAlphaValue, animated: true)
}

func headColorSliderSet(){

redSlider.setValue(headRedValue, animated: true)
greenSlider.setValue(headGreenValue, animated: true)
blueSlider.setValue(headBlueValue, animated: true)
alphaSlider.setValue(headAlphaValue, animated: true)
}

View & Image View:

🌟 若是要只有一個調控的影像,只需要將要變色的部分去背然後用 Image View 加進 Canvas。若是有兩個要調控的部分就要用到 View,留下要可以變色影像的部分,剩下都去背。

View提供了一個 parent view的角色,做層次上的變化

然後輸入程式碼,把影像跟 View 重疊在一起,要注意填滿的方式要一致。

Slider & Label:

🌟 要在 Label 顯示紅綠藍數值,Slider的範圍設定是0~1,所以在顯示時若是想顯示0~255,則值要乘255。Alpha 值顯示0~1則可直接顯示。

🌟 Slider範圍的設定,因對應不同功能而範圍不同。

🌟 在寫顯示整數的部分,有兩種寫法:

redValueLabel.text = String(Int(redSlider.value*255))
greenValueLabel.text = String(Int(greenSlider.value*255))
//整數也可以這樣寫
blueValueLabel.text = String(format:"%.0f", blueSlider.value*255)
alphaValueLabel.text = String(format: "%.1f", alphaSlider.value)

Switch:

🌟 在設定 if 條件時可運用一些運算子,例如:

&&:意思是 and

|| :意思是 or

…:代表範圍

//四個 Switch 要全是on,才能用 Random 及 Reset
if redSwitchFunction.isOn == true && greenSwitchFunction.isOn == true && blueSwitchFunction.isOn == true && alphaSwitchFunction.isOn == true {

randomFunction.isEnabled = true
resetFunction.isEnabled = true
}

完整程式碼:

//
// ViewController.swift
// 10-1 change mask colors
//
// Created by Ryan Lin on 2022/9/10.
//

import UIKit

class ViewController: UIViewController {


@IBOutlet weak var shadowView: UIView!
@IBOutlet weak var waterImageView: UIImageView!

@IBOutlet weak var hornsView: UIView!
@IBOutlet weak var headView: UIView!

@IBOutlet weak var segmentedControl: UISegmentedControl!

@IBOutlet weak var redSwitchFunction: UISwitch!
@IBOutlet weak var greenSwitchFunction: UISwitch!
@IBOutlet weak var blueSwitchFunction: UISwitch!
@IBOutlet weak var alphaSwitchFunction: UISwitch!

@IBOutlet weak var redSlider: UISlider!
@IBOutlet weak var greenSlider: UISlider!
@IBOutlet weak var blueSlider: UISlider!
@IBOutlet weak var alphaSlider: UISlider!

@IBOutlet weak var redValueLabel: UILabel!
@IBOutlet weak var greenValueLabel: UILabel!
@IBOutlet weak var blueValueLabel: UILabel!
@IBOutlet weak var alphaValueLabel: UILabel!

@IBOutlet weak var randomFunction: UIButton!
@IBOutlet weak var resetFunction: UIButton!

@IBOutlet weak var radiusSlider: UISlider!
@IBOutlet weak var frameSlider: UISlider!
@IBOutlet weak var shadowSlider: UISlider!


//用變數來儲存角和頭兩物件的各項對應Slider的初始值(使用者透過Slider被調整過後,則會存入新的值)
var hornsRedValue: Float = 0
var hornsGreenValue: Float = 1
var hornsBlueValue: Float = 1
var hornsAlphaValue: Float = 1

var headRedValue: Float = 1
var headGreenValue: Float = 1
var headBlueValue: Float = 1
var headAlphaValue: Float = 1

override func viewDidLoad() {
super.viewDidLoad()

let image = UIImage(named: "horns")
let hornsImageView = UIImageView(image: image)
hornsImageView.frame = hornsView.bounds
hornsImageView.contentMode = .scaleAspectFit
hornsView.mask = hornsImageView
//設定一開始畫面顯示角的顏色(與slider, button沒有連動)
hornsView.backgroundColor = UIColor(red: 0, green: 1, blue: 1, alpha: 1)

let headImageView = UIImageView(image: UIImage(named: "head"))
headImageView.frame = headView.bounds
headImageView.contentMode = .scaleAspectFit
headView.mask = headImageView
//設定一開始畫面顯示頭的顏色
headView.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
}

//讓 Label 顯示對應的 Slider value
func colorValueLabel() {

redValueLabel.text = String(Int(redSlider.value*255))
greenValueLabel.text = String(Int(greenSlider.value*255))
//整數也可以這樣寫
blueValueLabel.text = String(format:"%.0f", blueSlider.value*255)
alphaValueLabel.text = String(format: "%.1f", alphaSlider.value)
}
// 每次移動 Slider 後將對應的value儲存至原本變數中(slider數值存入顏色數值)
func hornsColorValueSave(){

hornsRedValue = redSlider.value
hornsGreenValue = greenSlider.value
hornsBlueValue = blueSlider.value
hornsAlphaValue = alphaSlider.value
}

func headColorValueSave(){

headRedValue = redSlider.value
headGreenValue = greenSlider.value
headBlueValue = blueSlider.value
headAlphaValue = alphaSlider.value
}

//用已儲存進變數的Value(新的顏色數值)讓Slider跳至相對應的位置(顏色數值存入slider數值)
func hornsColorSliderSet(){

redSlider.setValue(hornsRedValue, animated: true)
greenSlider.setValue(hornsGreenValue, animated: true)
blueSlider.setValue(hornsBlueValue, animated: true)
alphaSlider.setValue(hornsAlphaValue, animated: true)
}

func headColorSliderSet(){

redSlider.setValue(headRedValue, animated: true)
greenSlider.setValue(headGreenValue, animated: true)
blueSlider.setValue(headBlueValue, animated: true)
alphaSlider.setValue(headAlphaValue, animated: true)
}

//設定不同 Index 的 Segmented Control對應各自的 View
@IBAction func segmentValueSwitch(_ sender: Any) {

if segmentedControl.selectedSegmentIndex == 0{
hornsView.backgroundColor = UIColor(red: CGFloat(hornsRedValue), green: CGFloat(hornsGreenValue), blue: CGFloat(hornsBlueValue), alpha: CGFloat(hornsAlphaValue))

hornsColorSliderSet()
}
else if segmentedControl.selectedSegmentIndex == 1{
headView.backgroundColor = UIColor(red: CGFloat(headRedValue), green: CGFloat(headGreenValue), blue: CGFloat(headBlueValue), alpha: CGFloat(headAlphaValue))

headColorSliderSet()
}
colorValueLabel()
}

// slider 的變動
@IBAction func sliderValueChange(_ sender: Any) {
if segmentedControl.selectedSegmentIndex == 0{
hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

hornsColorValueSave()
}
else if segmentedControl.selectedSegmentIndex == 1{
headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headColorValueSave()
}
colorValueLabel()
}

//隨機顏色按鈕
@IBAction func randomButton(_ sender: Any) {

redSlider.setValue(.random(in: 0...1), animated: true)
greenSlider.setValue(.random(in: 0...1), animated: true)
blueSlider.setValue(.random(in: 0...1), animated: true)
alphaSlider.setValue(.random(in: 0.5...1), animated: true)

if segmentedControl.selectedSegmentIndex == 0{

hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

hornsColorValueSave()
}

else if segmentedControl.selectedSegmentIndex == 1{
headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headColorValueSave()
}
colorValueLabel()
}

//重新設定按鈕
@IBAction func resetButton(_ sender: UIButton) {

if segmentedControl.selectedSegmentIndex == 0{

hornsRedValue = 0
hornsGreenValue = 1
hornsBlueValue = 1
hornsAlphaValue = 1

hornsColorSliderSet()

hornsView.backgroundColor = UIColor(red: 0, green: 1, blue: 1, alpha: 1)

hornsColorValueSave()
}

else if segmentedControl.selectedSegmentIndex == 1{

headRedValue = 1
headGreenValue = 1
headBlueValue = 1
headAlphaValue = 1

headColorSliderSet()

headView.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)

headColorValueSave()
}
colorValueLabel()
}

//switch開關
@IBAction func colorsSwitch(_ sender: UISwitch) {

//四個 Switch 要全是on,才能用 Random 及 Reset
if redSwitchFunction.isOn == true && greenSwitchFunction.isOn == true && blueSwitchFunction.isOn == true && alphaSwitchFunction.isOn == true{

randomFunction.isEnabled = true
resetFunction.isEnabled = true
}
//在 Segmented Control Index = 0 或是 1 時
if segmentedControl.selectedSegmentIndex == 0 || segmentedControl.selectedSegmentIndex == 1{

//設定紅色的 Switch
if redSwitchFunction.isOn == true{

redSlider.isEnabled = true

}

//關掉顏色開關,數值變為0
if redSwitchFunction.isOn == false{

//讓角及頭的數值變成0
hornsRedValue = 0
headRedValue = 0

//讓紅色Slider數值變成0(新的顏色數值)
redSlider.setValue(0, animated: true)

//讓角及頭的影像紅色數值變0
hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))


//讓 Label 顯示對應的 Slider 數值
colorValueLabel()

//讓 Slider 不能滑動
redSlider.isEnabled = false

//停用 Random 及 Reset 功能
randomFunction.isEnabled = false
resetFunction.isEnabled = false
}

//設定綠色的 Switch
if greenSwitchFunction.isOn == true{

greenSlider.isEnabled = true

}
if greenSwitchFunction.isOn == false{

hornsGreenValue = 0
headGreenValue = 0

greenSlider.setValue(0, animated: true)

hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

colorValueLabel()

greenSlider.isEnabled = false

randomFunction.isEnabled = false
resetFunction.isEnabled = false
}

//設定藍色的 Switch
if blueSwitchFunction.isOn == true{

blueSlider.isEnabled = true

}

if blueSwitchFunction.isOn == false{

hornsBlueValue = 0
headBlueValue = 0

blueSlider.setValue(0, animated: true)


hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue:CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

colorValueLabel()

blueSlider.isEnabled = false

randomFunction.isEnabled = false
resetFunction.isEnabled = false
}

//設定透明度的 Switch
if alphaSwitchFunction.isOn == true{

alphaSlider.isEnabled = true

}

if alphaSwitchFunction.isOn == false{

hornsAlphaValue = 1
headAlphaValue = 1

alphaSlider.setValue(1, animated: true)

hornsView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

headView.backgroundColor = UIColor(red: CGFloat(redSlider.value), green: CGFloat(greenSlider.value), blue: CGFloat(blueSlider.value), alpha: CGFloat(alphaSlider.value))

colorValueLabel()

alphaSlider.isEnabled = false

randomFunction.isEnabled = false
resetFunction.isEnabled = false
}
}
}

@IBAction func radiusSliderChange(_ sender: Any) {

//waterImageView設圓角,後面shadowView會露出來,所以要一起設圓角
waterImageView.layer.cornerRadius = CGFloat(radiusSlider.value)
shadowView.layer.cornerRadius = CGFloat(radiusSlider.value)
//角會超出圓角,所以也一併設定
hornsView.layer.cornerRadius = CGFloat(radiusSlider.value)
}

@IBAction func frameSliderChange(_ sender: Any) {

waterImageView.clipsToBounds = true
waterImageView.layer.borderWidth = CGFloat(frameSlider.value)
waterImageView.layer.borderColor = CGColor(red: 1, green: (222/255), blue: (173/255), alpha: 0.5)
}

@IBAction func shadowSliderChange(_ sender: Any) {

shadowView.layer.shadowOpacity = Float(0.5)
shadowView.layer.shadowRadius = CGFloat(shadowSlider.value)
}

@IBAction func radiusSwitch(_ sender: UISwitch) {

if sender.isOn == true{
radiusSlider.isHidden = false
waterImageView.layer.cornerRadius = CGFloat(radiusSlider.value)
shadowView.layer.cornerRadius = CGFloat(radiusSlider.value)
}

else{

radiusSlider.isHidden = true
waterImageView.layer.cornerRadius = CGFloat(0)
shadowView.layer.cornerRadius = CGFloat(0)
}
}

@IBAction func frameSwitch(_ sender: UISwitch) {

if sender.isOn == true{
frameSlider.isHidden = false
waterImageView.layer.borderWidth = CGFloat(frameSlider.value)
}

else{

frameSlider.isHidden = true
waterImageView.layer.borderWidth = CGFloat(0)
}
}

@IBAction func shadowSwitch(_ sender: UISwitch) {

if sender.isOn == true{

shadowSlider.isHidden = false
shadowView.layer.shadowRadius = CGFloat(shadowSlider.value)
}

else{

shadowSlider.isHidden = true
shadowView.layer.shadowRadius = CGFloat(0)
}
}
}

--

--