用到的共同函式和 Extensions
import UIKit// MARK: - Label
func makeLabel() -> UILabel {
return makeLabel(withTitle: "")
}func makeLabel(withTitle title: String) -> UILabel {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = title
label.textAlignment = .center
label.textColor = .black
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
return label
}// MARK: - StackView
func makeVerticalStackView() -> UIStackView {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 32
return stack
}// MARK: - UIFont Extension
extension UIFont {
func withTraits(traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
let descriptor = fontDescriptor.withSymbolicTraits(traits)
return UIFont(descriptor: descriptor!, size: 0) //size 0 means keep the size as it is
}
}// MARK: - UIColor Extension
extension UIColor {
static let spotifyGreen = UIColor(red: 28/255, green: 184/255, blue: 89/255, alpha: 1)
}// MARK: - UIImage Extension
extension UIImage {
func resize(to goalSize: CGSize) -> UIImage? {
let widthRatio = goalSize.width / size.width
let heightRatio = goalSize.height / size.height
let ratio = widthRatio < heightRatio ? widthRatio : heightRatio
let resizedSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContextWithOptions(resizedSize, false, 0.0)
draw(in: CGRect(origin: .zero, size: resizedSize))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage
}
}// MARK: - Decimal Extension
extension Decimal {
var doubleValue: Double {
return NSDecimalNumber(decimal:self).doubleValue
}
}
把四種 Label 和一個 Button 擺入 Stack View
・Label 的文字部分(attributedText)要裝的是會回傳 NSAttributedString 類別物件的函式,後述。
・Spotify Button 使用下列函式:
func makeSpotifyButton(withText title: String) -> UIButton {
let button = UIButton()
let buttonHeight: CGFloat = 40
button.translatesAutoresizingMaskIntoConstraints = false let attributedText = NSMutableAttributedString(string: title, attributes: [
.font: UIFont.boldSystemFont(ofSize: 16),
.foregroundColor: UIColor.white,
.kern: 2
])
button.setAttributedTitle(attributedText, for: .normal)
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.minimumScaleFactor = 0.5 button.backgroundColor = .spotifyGreen
button.layer.cornerRadius = buttonHeight / 2 button.contentEdgeInsets = UIEdgeInsets(top: 10, left: buttonHeight, bottom: 10, right: buttonHeight) // iOS15 淘汰
return button
}
問卦與時間那兩行
注意可用 addAttribute 去新增 Attribute 的樣式、值、範圍。
圖片部分是利用 NSTextAttachment 類別的 image 屬性加入。
・調整圖片大小還可以使用 boundingRect,比較不直觀一點。let desiredWidth: CGFloat = 300let rect = rootString.boundingRect(with: CGSize(width: desiredWidth, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)attachment.bounds = CGRect(x: 0, y: -2, width: rect.height/2, height: rect.height/2)
段落式的內文
NSMutableParagraphStyle 擁有很多屬性可以負責調整各種留白、間距、對齊方式,如註解所示。
注意:The space between paragraphs is determined by adding the previous paragraph’s paragraphSpacing and the current paragraph’s paragraphSpacingBefore.
粗體文字
與問卦那行一樣,使用到 UIFont 的 extension 調整 symbolicTraits。
價錢文字
價錢以 Decimal 型別小數傳入。$、 /mo 符號和數字 1 小標用到 TextStyle 和 baselineOffset 的調整。
呼叫 amountAttributed(amount)
,它會回傳「數字部分」的 NSAttributedString。
該函式內最後要利用 makeFormattedBalance(dollars: String, cents: String)
回傳數字部分的樣式字串。
因此,函式開頭要先利用 formattedDollarsAndCents(_ amount: Decimal) -> (String, String)
將 Decimal 數值 8999.01 轉換成 (“8,999”, “.01”) 這樣的 tuple。其過程又用到
extension Decimal {
var doubleValue: Double {
return NSDecimalNumber(decimal:self).doubleValue
}
}
這個 Extension 把 Decimal 轉成 Double。
再來比較瑣碎,用到 modf、NumberFormatter 等手法進行一連串的剔除動作,可見程式註解。
參考資料
Jonathan Rasmusson(Ex-Spotify iOS Engineer)的影片和 Github
New value type based Attributed String introduced at WWDC 2021(存參)
Swift: Text Attachment Basics (2021, Xcode 13) — iOS(存參)
Swift: Number Formatter Tutorial (2021, Xcode 13) — iOS
使用 iOS 15 的 UIButton.Configuration 設定 button 樣式(存參)