皆さん、アイコン作ってますか?
僕は「デザイン勉強してお洒落なの作ってやるぜ!」とか思ってた時期もありましたが、今はもう単色背景+1文字でいいか、という感じになっています。
ガチなアプリだとそうはいかないかもしれませんが、個人アプリやハッカソン、TestFlight配布用の仮アイコンなどには十分です。
でもこうなってくると、もう、人が作るっていうレベルじゃないんですよね。色やテキスト、フォントとかを指定しすれば自動生成できちゃう。
実際、Ruby(RMagick)のスクリプト書いてやってたりしたんですが、せっかくならSwiftでやってしまおう、というのが今日のネタです。
(冒頭の画像も同じ方法で作りました。)
それPlaygroundで
最初はコマンドラインツールを作ろうかとも考えましたが、
- リアルタイムにプレビューできる
- 慣れたiOSの環境で作業できる
ということから、Playgroundでやることにしました。
(NSImage
よりUIImage
の方がいい…)
なお、Playgroundからはファイルシステムに自由にアクセスできませんが、Sandbox内は読み書きできるので、アイコンを作るぐらいなら不自由はありません。
※ sketchytech/SwiftFiles というUtilityもありましたが、今回は単純な処理しかしてないため使っていません。
IconCreatorを作る
ではさっそく、IconCreator
という雑なクラスを作ります。
Sources
の中に入れてもいいですが、それだとインタラクティブさが損なわれるので、メインのファイル(実体はContents.swift
)にそのまま書くことにします。
定数、変数の定義
保存するディレクトリやアイコンの色、テキストなどの情報を保持します。
class IconCreator {
let fileManager = NSFileManager.defaultManager()
let docPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
var rootPath: String {
return "\(docPath)/IconCreator/"
}
var backgroundColor = UIColor.grayColor()
var textColor = UIColor.whiteColor()
var lengths: [CGFloat] = [
// Phone
120.0,
180.0,
// Store
1024.0,
]
var fontScale: CGFloat = 0.75
var fontName = ".SFUIDisplay-Ultralight"
var string = "S"
初期化
ドキュメントディレクトリに画像保存用のフォルダを作成しています。
(既に存在した場合失敗するのでtry?
を使っています)
init() {
try? fileManager.createDirectoryAtPath(
rootPath,
withIntermediateDirectories: false,
attributes: nil
)
}
画像の作成
メインの処理です。
少し長いですが、
- コンテキストの作成・取得
- 背景色での塗りつぶし
- テキストの高さを取得して中央に描画
- UIImageの作成
をやっているだけです。
private func create(length: CGFloat) -> UIImage {
let size = CGSizeMake(length, length)
let rect = CGRectMake(0.0, 0.0, length, length)
let opaque = true
let scale: CGFloat = 1.0
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, backgroundColor.CGColor)
CGContextFillRect(context, rect)
let attributes = textAttributes(length)
let frame = string.boundingRectWithSize(
size,
options: [.UsesLineFragmentOrigin, .UsesFontLeading],
attributes: attributes,
context: nil
)
string.drawInRect(
CGRectOffset(
rect,
0.0,
CGRectGetMidY(rect) - CGRectGetMidY(frame)
),
withAttributes: attributes
)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
private func textAttributes(length: CGFloat) -> [String : AnyObject] {
let fontSize: CGFloat = length * fontScale
let defaultStyle = NSParagraphStyle.defaultParagraphStyle()
let style = defaultStyle.mutableCopy() as! NSMutableParagraphStyle
style.alignment = .Center
let attributes = [
NSFontAttributeName: UIFont(name: fontName, size: fontSize)!,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: style,
]
return attributes
}
画像のプレビュー・保存
preview
は作成した画像をそのまま配列で返し、save
はその画像をNSData
に変換して保存しています。
func preview() -> [UIImage] {
return lengths.map { create($0) }
}
func save() {
for length in lengths {
let image = create(length)
let filename = "icon\(Int(length)).png"
let data = UIImagePNGRepresentation(image)!
fileManager.createFileAtPath(
"\(rootPath)\(filename)",
contents: data,
attributes: nil
)
}
}
全体的にForced Unwrapping(!
)しまくってますが、このスクリプトは何かあったら落ちて欲しいので、そうしています。
動作確認
それでは、作成したIconCreator
を実際に使ってみたいと思います。
プレビュー
preview
を呼んでみます。
let creator = IconCreator()
creator.preview()
右端にあるQuick Look
かShow Result
アイコンをクリックすると、作成された画像を見ることができます。
一覧では縮小されちゃうので同じサイズに見えますが、それぞれの画像を開くと違うサイズになっていることがわかります。
保存
いよいよ作成したアイコンを実際に書き出します。
creator.save()
print("$ open \(creator.rootPath)")
Debug areaを表示すると、コンソールにopenコマンドが出力されているはずなので、Terminalにペーストして実行します。
$ open /var/folders/yc/yh1b0bsx5bv3qfxhg1k3zyj00000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.IconCreator-84F5EB55-FB19-4F28-8C1F-FF5981176470/Documents/IconCreator/
無事、保存されているようです。
カスタムフォント
Playgroundではカスタムフォントも使えます。
参考:Custom fonts in Xcode playground - Stack Overflow
例として、Google FontsからPacificoを借りてきます。
(見た目の変化がわかりやすいようにHandwritingなフォントを選びました。)
XcodeのProject navigatorを開いて、
.ttf
ファイルをResources
にドラッグ&ドロップします。
あとはIconCreatorにloadFont
というメソッドを追加します。
class func loadFont(name: String) {
let font = NSBundle.mainBundle().URLForResource(name, withExtension: nil)!
CTFontManagerRegisterFontsForURL(font, CTFontManagerScope.Process, nil)
}
呼び出してみます。
let creator2 = IconCreator()
IconCreator.loadFont("Pacifico.ttf")
creator2.fontName = "Pacifico"
creator2.fontScale = 0.6
creator2.preview()
Sの文字が変わりました。(そのままだと切れてしまったのでフォントサイズも変えています。)
アイコンフォント
カスタムフォントが使えるということは、アイコンフォントも使えます。
例えば、Font Awesomeのフォントを持ってきて同じようにやると…
let creator3 = IconCreator()
IconCreator.loadFont("FontAwesome.otf")
creator3.fontName = "FontAwesome"
creator3.string = "\u{f09b}"
creator3.preview()
GitHubなアイコンになりました。
ハマったところ
UIFont.systemFontOfSize(UIFont.systemFontSize(), weight: UIFontWeightUltraLight).fontName
だと、.SFUIText-Light
が、
UIFont.systemFontOfSize(100.0, weight: UIFontWeightUltraLight).fontName
とかだと、.SFUIDisplay-Ultralight
が返ってきて困りました。
同じweight
を指定してもfontSize
によって返ってくるフォント名が変わります。結局、デフォルトのフォントは決め打ちしました。
注意
- 実際にアプリに使う場合は、フォントのライセンスを要確認です。
- Pathの文字列を編集してる時にPlaygroundが実行されると、中途半端な名前のファイルができたりします。
ソースコード
GitHubで公開しています。
https://github.com/tnantoka/IconCreator
まとめ
いかがでしたでしょうか?
Playground、久々にちゃんと触りましたが初期の頃より安定してて良い感じでした。
Xcodeのアップデートでファイルアクセスに制限がかかったりしないことを祈ります。
それでは。