④④使用圖片,文字,emoji,邊框 & 圓角製作可愛吃貨卡🍖
在 playground 進行
一直覺得這一題很有趣,但我卻遲遲不敢開始做。因為同學跟學長姊們的設計都很有質感,但我美術細胞實在是 0 … 不過看在今天是七夕的份上,幫有廣大迷妹的貪吃鬼 66 做張吃貨卡吧!
創建存放照片的 ImageView
新建一個 playground 檔案後,先將照片放進 Resources。
創建一個 dogImageView
import UIKit
let dogImageView = UIImageView(image: UIImage(named: "66與口水.png"))
大亂練:使用 UIView 的屬性調整元件基本特性
UIImage 圖片相關-
frame 設定大小位置
dogImageView.frame = CGRect(x: 0, y: 0, width: 375, height: 500)
如果寬高沒有跟原圖差不多,會造成圖片被擠壓
如果不清楚比例,也怕在設定的時候變形,使用屬性 contentMode 設定
contentMode
在練習 storyboard 的時候,使用所有 contentMode 做過比較,如果不清楚它怎麼改變照片顯示的樣子,可以先看這篇:
scaleAspectFit 維持比例,但不會填滿畫面
// ImageView 改為正方形,以測試 contentMode 的效果
dogImageView.frame = CGRect(x: 0, y: 0, width: 375, height: 375)
dogImageView.contentMode = .scaleAspectFit
scaleAspectFill 維持比例填滿 frame 畫面,但超出畫面的會被截掉
dogImageView.contentMode = .scaleAspectFill
不能用:scaleToFill 填滿畫面,但比例會跑掉(看起來預設 Image 就是 scaleToFill)
dogImageView.contentMode = .scaleToFill
透明度 alpha
範圍是 0–1。數字越小越透明
// frame 回到原來寬高
dogImageView.frame = CGRect(x: 0, y: 0, width: 375, height: 500)
// 透明度
dogImageView.alpha = 0.3
dogImageView.alpha = 0.5
UILabel 文字相關-
text 文字
// 設定儲存文字的常數為 UILabel 類別,並設定位置大小
let messageLabel = UILabel(frame: CGRect(x: 100, y: 100, width: 150, height: 30))
// 存入文字
messageLabel.text = "等肉條等到睡著"
textColor 文字顏色
messageLabel.textColor = UIColor(red: 102/255, green: 102/255, blue: 1, alpha: 1)
font 文字大小
messageLabel.font = UIFont.systemFont(ofSize: 12)
CALayer 控制邊框寬度、顏色與圓角
borderWidth 邊框寬度
dogImageView.layer.borderWidth = 10
// 因為layer.borderWidth 沒辦法直接預覽,再呼叫一次 dogImageView 看結果
dogImageView
borderColor 邊框顏色
深色模式的背景讓黑色邊框很不明顯,換一下顏色試試:
dogImageView.layer.borderColor = CGColor(red: 210/255, green: 105/255, blue: 30/255, alpha: 1)
dogImageView
圓角邊框 cornerRadius
dogImageView.layer.cornerRadius = 50
結果照片沒有一起圓角,凸出來了。要將 clipsToBounds 設為 True 才行:
dogImageView.clipsToBounds = true
加入 emoji
// 先把狗狗透明度改回不透明,不然看起來好昏暗
dogImageView.alpha = 1
let meatOnBone = UILabel(frame: CGRect(x: 50, y: 420, width: 50, height: 50))
meatOnBone.text = "🍖"
meatOnBone.font = UIFont.systemFont(ofSize: 50)
// 把骨頭肉加到狗狗圖片裡,成為狗狗圖片的 subview
dogImageView.addSubview(meatOnBone)
旋轉
transform + CGAffineTransform + rotationAngle
將文字旋轉在之前畫巴西國旗上面的葡文時有做過:
.pi 代表 180 度,1 度是 .pi/180,因此 90 度是 .pi / 180 * 90。
meatOnBone.transform = CGAffineTransform(rotationAngle: .pi / 180 * 90)
// 呼叫圖片看預覽
dogImageView
這次練習讓骨頭肉有更多動作與變化,參考文章:
縮放
transform + CGAffineTransform + scale
// X 與 Y 寬高兩方向同時放大 2 倍
meatOnBone.transform = CGAffineTransform(scaleX: 2, y: 2)
dogImageView
旋轉 + 縮放
如果要對骨頭肉同時做旋轉與縮放的動作,不能直接用前方的程式串接,圖片只會顯示最後一次對 transform 的更改。我們有兩個方式可以串接:
方法一
meatOnBone.transform = CGAffineTransform(rotationAngle: .pi / 180 * 90)
meatOnBone.transform = meatOnBone.transform.scaledBy(x: 2, y: 2)
dogImageView
第一種方式是先用 CGAffineTransform 設定旋轉角度之後,再使用 scaledBy 更改旋轉角度。
scaledBy 本名其實是 CGAffineTransformScale,他是一個 instance method。用來在一個已存在的 CGAffineTransform(仿射變換)上,不修改原始的仿射變換,而返回一個新的變換。
方法二
meatOnBone.transform = meatOnBone.transform.concatenating(CGAffineTransform(rotationAngle: .pi / 180 * 90).scaledBy(x: 2, y: 2))
dogImageView
第二種方式更簡潔,只要一行程式。concatenating 的本名是 CGAffineTransformConcat,它用來將兩個仿射變換組合在一起,形成一個新的變換。
成果
回到沒有任何效果的 CGAffineTransform
骨頭肉會回到原本的大小與方向,再做其他變化。
meatOnBone.transform = CGAffineTransform.identity
mirror 鏡像翻轉
我們再創建一塊骨頭肉,用來實驗鏡像翻轉。
// 在骨頭肉同樣的位置創建一個 mirrorMeatOnBone,設定同骨頭肉
let mirrorMeatOnBone = UILabel(frame: CGRect(x: 50, y: 420, width: 50, height: 50))
mirrorMeatOnBone.text = "🍖"
mirrorMeatOnBone.font = UIFont.systemFont(ofSize: 50)
// 將鏡像骨頭肉的左上 x 座標值設在骨頭肉最右邊 x 座標值的右邊 10 單位
mirrorMeatOnBone.frame.origin.x = meatOnBone.frame.maxX + 10
// 將鏡像骨頭肉 x 軸的值變成 -1 倍,會變成鏡像
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: -1, y: 1)
// 將鏡像骨頭肉加回狗狗圖裡
dogImageView.addSubview(mirrorMeatOnBone)
dogImageView
y 軸也可以翻轉,只是注意 y 軸方向越下方數字越大。將剛剛 x 軸鏡像翻轉的那兩行程式修改一下,變成垂直翻轉:
// 因為原本骨頭肉最高點是他 y 軸的最小值,因此 maxX 要變成 minY
mirrorMeatOnBone.frame.origin.y = meatOnBone.frame.minY - 50
// 因為要改成垂直翻轉,所以是改變 y 軸變成 -1
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: 1, y: -1)
translation 位移
骨頭肉真的要逃走了,meatOnBone 透過 transform + CGAffineTransform + init(translationX:y:),調整 x 與 y 軸移動的單位。
meatOnBone.transform = CGAffineTransform(translationX: -35, y: 25)
結合縮放、旋轉與位移
最後當然什麼都要加一加,我們之前結合過縮放與旋轉,現在再試著加上位移。那因為這三個變換都是數學的運算,因此前後順序會影響到結果。尤其是先位移再縮放跟先縮放再位移會是不一樣的。
方法一
// identity 將之前變化的復原,接著執行平移、縮放與旋轉
meatOnBone.transform = CGAffineTransform.identity.translatedBy(x: -35, y: 25).scaledBy(x: 0.5, y: 0.5).rotated(by: .pi / 180 * 135)
方法二
與前面的方法二相同,先定義三種屬性,再使用 concatenating 一個一個串起來。注意先後順序與方法一相反。這樣出來的樣式是一樣的:
let translateTransform = CGAffineTransform(translationX: -35, y: 25)
let scaleTransform = CGAffineTransform(scaleX: 0.5, y: 0.5)
let rotateTransform = CGAffineTransform(rotationAngle: .pi/180 * 135)
meatOnBone.transform = rotateTransform.concatenating(scaleTransform).concatenating(translateTransform)
dogImageView
上述是各種可使用的方法。
最終的吃貨卡:
不知道使用吃貨卡可以招喚出什麼…
程式:
import UIKit
let dogImageView = UIImageView(image: UIImage(named: "66與口水.png"))
dogImageView.frame = CGRect(x: 0, y: 0, width: 375, height: 500)
var messageLabel = UILabel(frame: CGRect(x: 160, y: 280, width: 200, height: 50))
messageLabel.text = "吃貨卡"
messageLabel.textColor = UIColor(red: 210/255, green: 105/255, blue: 30/255, alpha: 1)
//文字使用系統粗體
messageLabel.font = UIFont.boldSystemFont(ofSize: 50)
dogImageView.addSubview(messageLabel)
//設定邊框
dogImageView.layer.borderWidth = 10
dogImageView.layer.borderColor = CGColor(red: 210/255, green: 105/255, blue: 30/255, alpha: 1)
dogImageView.layer.cornerRadius = 50
dogImageView.clipsToBounds = true
var meatOnBone = UILabel(frame: CGRect(x: 52, y: 420, width: 50, height: 50))
meatOnBone.text = "🍖"
meatOnBone.font = UIFont.systemFont(ofSize: 30)
dogImageView.addSubview(meatOnBone)
meatOnBone.transform = CGAffineTransform(scaleX: 2, y: 2)
meatOnBone = UILabel(frame: CGRect(x: 52, y: 370, width: 50, height: 50))
meatOnBone.text = "🍖"
meatOnBone.font = UIFont.systemFont(ofSize: 30)
dogImageView.addSubview(meatOnBone)
meatOnBone.transform = CGAffineTransform(scaleX: 2, y: 2)
meatOnBone = UILabel(frame: CGRect(x: 52, y: 320, width: 50, height: 50))
meatOnBone.text = "🍖"
meatOnBone.font = UIFont.systemFont(ofSize: 30)
dogImageView.addSubview(meatOnBone)
meatOnBone.transform = CGAffineTransform(scaleX: 2, y: 2)
meatOnBone = UILabel(frame: CGRect(x: 52, y: 270, width: 50, height: 50))
meatOnBone.text = "🍖"
meatOnBone.font = UIFont.systemFont(ofSize: 30)
dogImageView.addSubview(meatOnBone)
meatOnBone.transform = CGAffineTransform(scaleX: 2, y: 2)
// 對角鏡像骨頭肉們
var mirrorMeatOnBone = UILabel(frame: CGRect(x: 278, y: 30, width: 50, height: 50))
mirrorMeatOnBone.text = "🍖"
mirrorMeatOnBone.font = UIFont.systemFont(ofSize: 30)
// 將鏡像骨頭肉 x 軸跟 y 軸的值變成 -2 倍,會變成對角鏡像(2因為前面的骨頭肉都是兩倍大小)
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: -2, y: -2)
// 將鏡像骨頭肉加回狗狗圖裡
dogImageView.addSubview(mirrorMeatOnBone)
mirrorMeatOnBone = UILabel(frame: CGRect(x: 278, y: 80, width: 50, height: 50))
mirrorMeatOnBone.text = "🍖"
mirrorMeatOnBone.font = UIFont.systemFont(ofSize: 30)
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: -2, y: -2)
dogImageView.addSubview(mirrorMeatOnBone)
mirrorMeatOnBone = UILabel(frame: CGRect(x: 278, y: 130, width: 50, height: 50))
mirrorMeatOnBone.text = "🍖"
mirrorMeatOnBone.font = UIFont.systemFont(ofSize: 30)
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: -2, y: -2)
dogImageView.addSubview(mirrorMeatOnBone)
mirrorMeatOnBone = UILabel(frame: CGRect(x: 278, y: 180, width: 50, height: 50))
mirrorMeatOnBone.text = "🍖"
mirrorMeatOnBone.font = UIFont.systemFont(ofSize: 30)
mirrorMeatOnBone.transform = CGAffineTransform(scaleX: -2, y: -2)
dogImageView.addSubview(mirrorMeatOnBone)
dogImageView
Reference: