#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:
設定 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 重疊在一起,要注意填滿的方式要一致。
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
}
Reference:
完整程式碼:
//
// 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)
}
}
}