Railsでguard + RSpec の通知にterminal-notifierを使う方法

Railsでguard + RSpec の通知にterminal-notifierを使う方法。

Ruby on Railsチュートリアルを進めていくと、
3章のGuardの説明で、MacユーザーはGrowlをAppStoreで購入するよう書いてある。(必須ではない)
これ通知センターでできないの?と思ったので調べてみたら terminal-notifier で出来ました。

terminal-notifierのインストール

$ brew install terminal-notifier

Gemfileの編集

group :test do
    gem 'selenium-webdriver', '2.35.1'
    gem 'capybara', '2.1.0'
    gem 'terminal-notifier-guard', '1.6.3'
end

bundle install

$ bundle install

guardの実行

$ bundle exec guard

これで通知が飛んできます。

Swiftでフォントを動的に登録する方法

フォントを動的に読み込む手順について。
たまにフォントを動的に読み込んで使いたい時がでてきます。
たとえば・・・

  • iOSアプリの配布時サイズを抑えるためにフォントをダウンロードさせる場合
  • bundle(Xcode pluginなど)にフォントを含めたい場合

などです。

フォントを動的に登録する

let data = NSData(contentsOfFile: fontPath)
var error: Unmanaged<CFError>?
let provider = CGDataProviderCreateWithCFData(data)
let registerFont: CGFont = CGFontCreateWithDataProvider(provider)
if CTFontManagerRegisterGraphicsFont(registerFont, &error) == false {
// error
}

フォントの登録を解除する

CTFontManagerUnregisterGraphicsFont(registerFont, &error)

基本的にリソースに含めてしまって良いと思いますが、
覚えておいて損はないです。

SwiftでフォントをNSImageに書き込む方法

SwiftでフォントをNSImageに書き込む方法。

NSMutableAttributedStringを使ってBitmapに文字を書き込みます。

let size = CGSizeMake(32, 32)
if let font = NSFont(name: "AppleGothic", size: 24) {
let attrs: [NSObject: AnyObject] = [NSFontAttributeName: font]
var attribute = NSMutableAttributedString(string: "A", attributes: attrs)
let rect = getRect(attribute, size: size)

if let offscreenRep = NSBitmapImageRep(bitmapDataPlanes: nil,
pixelsWide: width,
pixelsHigh: height,
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: NSDeviceRGBColorSpace,
bitmapFormat: NSBitmapFormat.NSAlphaFirstBitmapFormat,
bytesPerRow: 0,
bitsPerPixel: 0),
let g = NSGraphicsContext(bitmapImageRep: offscreenRep) {
// begin --------
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.setCurrentContext(g)

let ctx = g.CGContext
CGContextSetRGBFillColor(ctx, 0, 0, 0, 0.0)
CGContextFillRect(ctx, CGRectMake(0, 0, size.width, size.height))

// 文字を書き込む
attribute.drawInRect(rect)

// end ----------
NSGraphicsContext.restoreGraphicsState()

// NSImageに設定する
let img = NSImage(size: size)
img.addRepresentation(offscreenRep)
}
}

private func getRect(attributeString: NSAttributedString, size: CGSize) -> CGRect {
let iconSize = attributeString.size
let xoff = (size.width - iconSize.width) / 2.0
let yoff = (size.height - iconSize.height) / 2.0

return CGRectMake(xoff, yoff, iconSize.width, iconSize.height)
}

背景色を変えたい場合は CGContextSetRGBFillColor のRGBAを調整することで実現可能です。

SwiftでXcodeプラグイン - Windowを作る方法

SwiftでXcodeプラグインのウィンドウを作る方法。

xibの作成

Windowを選択してxibを作成します。

ViewControllerの作成

ViewControllerを作成して、xibの Placeholders > File's Owner を選択。
Identity InspectorCustom Class > Class に作成したViewControllerを指定。

Windowを作成する

前回追加したプラグインのメニューを選択した時に実行されるコードとして
Windowを作成・表示するコードを書きます。

var windowController: MyWindowViewController?

func show(sender: AnyObject?) {
if self.windowController?.window == nil {
self.windowController = IconGenerateWindowController()
}

self.windowController?.window?.makeKeyAndOrderFront(self)
}

SwiftでXcodeプラグイン - メニューを作る方法

SwiftでXcodeプラグインのメニューを作る方法。

メニューの追加方法はMacAppと共通ですが、 NSNotificationCenter を介すのがポイントです。
pluginDidLoad が呼ばれた時点では NSApp.mainMenu が作成されていないので
直接呼び出すと追加できません。

class MyPlugin: NSObject {
static let sharedPlugin = MyPlugin()

class func pluginDidLoad(plugin: NSBundle) {
if let appName = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as? String {
if appName == "Xcode" {
sharedPlugin.bundle = plugin
NSNotificationCenter.defaultCenter().addObserver(sharedPlugin, selector: "addMenuItems:", name: NSApplicationDidFinishLaunchingNotification, object: nil)
}
}
}

func addMenuItems(notification: NSNotification?) {
let rootMenuName = "Plugin"

var item = NSMenuItem(title: "MyPluginMenu", action: "show:", keyEquivalent: "")
item.target = self

if let mainMenu = NSApp.mainMenu ?? nil {
if let pluginMenu = mainMenu.itemWithTitle(rootMenuName) {
// すでに存在するメニューに追加する
pluginMenu.submenu?.addItem(item)
} else {
// 新しくメニューを作成して追加する
var pluginMenu = NSMenu(title: rootMenuName)
pluginMenu.addItem(item)

var newMenuItem = NSMenuItem(title: rootMenuName, action: nil, keyEquivalent: "")
newMenuItem.submenu = pluginMenu
mainMenu.addItem(newMenuItem)
}
}
}

func show(sender: AnyObject?) {
NSLog("Select menu")
}
}

SwiftでXcodeプラグインを作るための手順

SwiftでXcodeプラグインを作るための手順。
Xcode6.4(6E35b)で確認しました。

projectの作成

Create a new Xcode project
OS X > Framework & Library > Bundle

Product Name: [PRODUCT_NAME]
Bundle Extension: xcplugin

Bundle Extensionは成果物の拡張子です。

info.plistの編集

info.plistに下記設定を追加します。

key type value
XCGCReady Boolean YES
XCPluginHasUI Boolean NO
XC5Compatible Boolean YES
XC4Compatible Boolean YES
DVTPlugInCompatibilityUUIDs Array 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90

Build Settingsの編集

Build Settingsを下記の通り設定します。

setting value
Installation Build Products Location $(HOME)
Installation Directory /Library/Application Support/Developer/Shared/Xcode/Plug-ins
Deployment Location YES
Embedded Content Contains Swift Code YES
Skip Install NO

コードを書く

任意の名前のSwiftファイルを追加してコードを記述します。

import AppKit

class MyPlugin: NSObject {
class func pluginDidLoad(plugin: NSBundle) {
NSLog("Hello World!")
}
}

起動時に pluginDidLoad: が呼ばれるので、ここを起点に拡張していきます。

動作確認

ビルドしたら下記ディレクトリにプラグインが作成されます。

~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/

Xcodeを再起動すると下記の警告メッセージが表示されるので Load Bundle を選択します。

Unexpected code bundle “[PLUGIN_NAME].xcplugin”

Console.appを開いて Hello World! と出力されていれば完了です。
または /var/log/system.log を直接確認してもOKです。

Swiftのclass funcとstatic funcの違い

クラスメソッド/プロパティに指定できるclassとstaticの違いについて。
どちらもクラスメソッド/プロパティとして定義するときに使えますが
継承先のクラスでオーバーライド可能かどうかが異なります。

種類 オーバーライド
class func
static func ×

オーバーライドの可否

class func はオーバーライド可、 static func は不可。

ちなみにstatic funcfinal class func と同義ですが
入り乱れると誤解を招くのでどちらを使うか事前に決めておいた方が良いです。

SwiftでTwitterへ投稿する

Social.frameworkを使います。

Twitterへ投稿する

import Social

override func viewDidAppear(animated: Bool) {
let tweetVC = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
tweetVC.setInitialText("tweet text")
self.presentViewController(tweetVC, animated: true, completion: nil)
}

サンプルではビュー表示直後に呼び出していますが、
実際はボタンタップのイベントで呼ぶことが多いと思います。

結果によって何か処理をする

投稿結果を見て何か処理をしたい場合はcompletionHandlerにクロージャを設定しておきます。
投稿されるかキャンセルされると呼び出されます。

tweetVC.completionHandler = { result in
switch result {
case .Done:
// 投稿された
break
case .Cancelled:
// cancelされた
break
}
}

self.presentViewController(tweetVC, animated: true, completion: nil)

XCodeでコードを書かずにUIImageViewを角丸にする方法

User Defined Runtime Attributesを下記の通り設定するだけ。

Key Path Type Value
layer.cornerRadius Number 20
layer.masksToBounds Boolean x

仕組みとしてはKVOを用いて設定されるので、
UIViewを継承するなどして自分で定義したメンバ変数の値もここで設定できます。

@IBInspectableでもう少しコード感をなくす

UIViewをextensionして @IBInspectable をつけてラップしてあげると
Attributes Inspectorで設定できるようになるのでアートスタッフに優しくなります。

extension UIView {
@IBInspectable var cornarRadius: CGFloat {
get {
return self.layer.cornerRadius
}

set {
self.layer.cornerRadius = newValue
self.layer.masksToBounds = true
}
}
}

前者の方法だとだいたい書き方を忘れて調べることになるので
後者の方法が良いかもしれません。
角丸に限らずその他のプロパティも編集できるよう定義しておくと便利です。

Swift2のSequenceType: (for in)

クラスインスタンスをfor…inでサーチ可能にする方法。
Swift2で微妙に変わってAnyGeneratorを使用するようになっています。

Swift2でSequenceType

class MyClass {
int contents: [Int] = []
}

extension MyClass: SequenceType {
public typealias Generator = AnyGenerator<MyClass>
public func generate() -> Generator {
var index = 0
return anyGenerator {
if index < self.contents.count {
return self.contents[index++]
}
return nil
}
}
}

Swift1.2でSequenceType

class MyClass {
int contents: [Int] = []
}

extension MyClass: SequenceType {
public typealias Generator = GeneratorOf<MyClass>
public func generate() -> Generator {
var index = 0
return GeneratorOf {
if index < self.contents.count {
return self.contents[index++]
}
return nil
}
}
}

Snippet

// Swift2
extension <# MyClass #>: SequenceType {
public typealias Generator = AnyGenerator<<# MyClass #>>
public func generate() -> Generator {
var index = 0
return anyGenerator {
if index < self.<# ContentsCount #> {
return self.<# Contents #>[index++]
}
return nil
}
}
}

// Swift1.2
extension <# MyClass #>: SequenceType {
public typealias Generator = GeneratorOf<<# MyClass #>>
public func generate() -> Generator {
var index = 0
return GeneratorOf {
if index < self.<# ContentsCount #> {
return self.<# Contents #>[index++]
}
return nil
}
}
}