42
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift その3Advent Calendar 2015

Day 10

アイコンもPlaygroundで作っちゃおう

Last updated at Posted at 2015-12-09
icon256.png

皆さん、アイコン作ってますか?

僕は「デザイン勉強してお洒落なの作ってやるぜ!」とか思ってた時期もありましたが、今はもう単色背景+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 LookShow Resultアイコンをクリックすると、作成された画像を見ることができます。
一覧では縮小されちゃうので同じサイズに見えますが、それぞれの画像を開くと違うサイズになっていることがわかります。

preview.png

保存

いよいよ作成したアイコンを実際に書き出します。

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/

無事、保存されているようです。

open.png

カスタムフォント

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の文字が変わりました。(そのままだと切れてしまったのでフォントサイズも変えています。)

pacifico.png

アイコンフォント

カスタムフォントが使えるということは、アイコンフォントも使えます。
例えば、Font Awesomeのフォントを持ってきて同じようにやると…

let creator3 = IconCreator()
IconCreator.loadFont("FontAwesome.otf")
creator3.fontName = "FontAwesome"
creator3.string = "\u{f09b}"
creator3.preview()

GitHubなアイコンになりました。

fontawesome.png

ハマったところ

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のアップデートでファイルアクセスに制限がかかったりしないことを祈ります。

それでは。

42
42
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?