ZOZOTOWN iOS の内部で利用している UI/NS 系クラス拡張につきまして、ニッチすぎる内容を除き紹介させていただきます!
※下記のコードはすべて、 Swift 4.2 / iOS12 SDK で動作するものです。
Array+Safe
配列の範囲外にアクセスしたさい、クラッシュせず nil
を返して欲しい場合に利用します。
extension Array {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
Use Case
func xxx(index: Int) {
let list: [String] = ["a", "b", "c"]
if let item = list?[safe: index] {
...
}
}
String+HtmlMutableAttributedString
HTMLが格納されている String
をHTML形式の NSMutableAttributedString
に変換するさいに利用します。
※メインスレッド以外で利用するとクラッシュするため、ご注意ください。
extension String {
var htmlMutableAttributedString: NSMutableAttributedString? {
if let encodedData = data(using: .unicode, allowLossyConversion: true) {
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [.documentType: NSAttributedString.DocumentType.html]
let htmlText = try? NSMutableAttributedString(data: encodedData, options: options, documentAttributes: nil)
return htmlText
}
return nil
}
}
NSMutableParagraphStyle+Initializer
NSAttributedText
を作成するさいに利用します。
NSAttributedTextを作成するさい、
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 6
paragraph.textAlignment = .center
paragraph.lineBreakMode = .byWordWrapping
上記がリーダブルではなく、コードを書く手間を削減したかったので、下記のように簡単に書けるようにしました。
extension NSMutableParagraphStyle {
convenience init(lineSpacing: CGFloat? = nil, textAlignment: NSTextAlignment? = nil, lineBreakMode: NSLineBreakMode? = nil, lineHeight: CGFloat? = nil) {
self.init()
if let lineSpacing = lineSpacing {
self.lineSpacing = lineSpacing
}
if let textAlignment = textAlignment {
self.alignment = textAlignment
}
if let lineBreakMode = lineBreakMode {
self.lineBreakMode = lineBreakMode
}
if let lineHeight = lineHeight {
self.maximumLineHeight = lineHeight
self.minimumLineHeight = lineHeight
}
}
}
Use Case
func xxx() {
let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 14),
.foregroundColor: UIColor.black,
.paragraphStyle: NSMutableParagraphStyle(lineSpacing: 6, textAlignment: .center, lineBreakMode: .byWordWrapping)]
...
}
UIColor+Hex
CSSで利用されるような16進数表記文字列( #808080
など)を UIColor
にするさいに利用します。
extension UIColor {
public convenience init(hex: Int, alpha: CGFloat) {
let b = CGFloat((hex & 0x000000ff) >> 0) / 255.0
let g = CGFloat((hex & 0x0000ff00) >> 8) / 255.0
let r = CGFloat((hex & 0x00ff0000) >> 16) / 255.0
self.init(red: r, green: g, blue: b, alpha: alpha)
}
public convenience init(hexWithAlpha: UInt32) {
let b = CGFloat((hexWithAlpha & 0x000000ff) >> 0) / 255.0
let g = CGFloat((hexWithAlpha & 0x0000ff00) >> 8) / 255.0
let r = CGFloat((hexWithAlpha & 0x00ff0000) >> 16) / 255.0
let a = CGFloat((hexWithAlpha & 0xff000000) >> 24) / 255.0
self.init(red: r, green: g, blue: b, alpha: a)
}
public convenience init(hex: Int) {
self.init(hex: hex, alpha: 1.0)
}
public convenience init(hexString: String) {
var rgbString = hexString.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if rgbString.hasPrefix("#") {
rgbString.remove(at: rgbString.startIndex)
}
switch rgbString.count {
case 8:
var argbValue: UInt32 = 0
Scanner(string: rgbString).scanHexInt32(&argbValue)
self.init(hexWithAlpha: argbValue)
case 6:
var rgbValue: UInt32 = 0
Scanner(string: rgbString).scanHexInt32(&rgbValue)
self.init(hex: Int(rgbValue))
default:
self.init(hex: 0)
}
}
}
Use Case
func xxx() {
let hexWithAlphaColor1 = UIColor(hex: 0xffffff, alpha: 0.8) // rgba: 1.0 / 1.0 / 1.0 / 0.8
let hexWithAlphaColor2 = UIColor(hexWithAlpha: 0xffffffcc) // rgba: 1.0 / 1.0 / 1.0 / 0.8
let hexColor = UIColor(hex: 0xffffff) // rgba: 1.0 / 1.0 / 1.0 / 1.0
let stringColor1 = UIColor(hexString: "ffffff") // rgba: 1.0 / 1.0 / 1.0 / 1.0
let stringColor2 = UIColor(hexString: "#ffffff") // rgba: 1.0 / 1.0 / 1.0 / 1.0
let stringColor3 = UIColor(hexString: "#ffffffcc") // rgba: 1.0 / 1.0 / 1.0 / 0.8
}
URL+Queries
APIに GET
リクエストを送信する場合など、クエリーパラメーターを Dictionary
で設定するさいに利用します。
extension URL {
func addQueries(_ queries: [String: String]) -> URL? {
if queries.isEmpty {
return self
} else {
if var urlComponents = URLComponents(string: absoluteString) {
urlComponents.queryItems = (urlComponents.queryItems ?? []) + queries.map({ URLQueryItem(name: $0.key, value: $0.value) })
return urlComponents.url
} else {
return self
}
}
}
}
Use Case
func writeAppStoreReview() {
guard let appStoreUrl = URL(string: String(format: "itms-apps://itunes.apple.com/app/id%@", "(Apple ID)")) else {
return
}
if let appStoreWithWriteReviewUrl = appStoreUrl.addQueries(["action": "write-review"]) {
_ = openURL(appStoreWithWriteReviewUrl)
}
}
おまけ:NSURL+Queries
Objective-C のコードから上記 URL+Queries
をそのまま利用できなかったため、 NSURL
版も作成しました。
// MARK: - Definition
extension NSURL {
@objc func addQueries(_ queries: [String: String]) -> NSURL? {
return (self as URL).addQueries(queries) as NSURL?
}
}
最後に
もしご自身のプロダクトに使えそうなら、ご利用いただけると幸いです!