読者です 読者をやめる 読者になる 読者になる

【android】エミュレータのhostsを書き換える

Android Debug Bridge (adb) を使用して ローカルにあるhostsファイルをエミュレータ上に配信させて書き換えます。

まず、エミュレータをroot権限で操作できるように設定します。

$ adb root

remountします。

$ adb remount

127.0.0.1のまま差し替えちゃうとエミュレータ自身を見に行ってしまうので、自PCのプライベートIPに差し替えます。
(192.xxxの部分は適宜書き換えてください)

$ cat /etc/hosts | gsed 's/127.0.0.1/192.168.1.9/' > /tmp/hosts-adb

エミュレータ上にhostsを配信します。

$ adb push /tmp/hosts-adb /system/etc/hosts

これで、androidエミュレータのhostsを書き換えることが出来ました。

【swift】コードでAutoLayout その1 - Visual Format Language

コードでAutoLayoutを記述します。

iOSにはVisual Format LanguageというAutoLayoutを直感的に定義できるフォーマットが用意されており、使い方さえ覚えてしまえばちょっとした制約であればサクッとコードで書けてしまいます。

developer.apple.com

使ってみる

とりあえず、AutoLayoutを適用するためのViewを用意してみます。

let wrapperView = UIView()
wrapperView.backgroundColor = .redColor()
wrapperView.translatesAutoresizingMaskIntoConstraints  = false
        
view.addSubview(wrapperView)

この時、大事なポイントが二つあります。

1. translatesAutoresizingMaskIntoConstraints を falseにする

詳しくは理解していないですが、AutoLayoutを使用する場合は、昔からあるレイアウトシステムのAutoresizingMaskをfalseにする必要があります。

2. AutoLayoutの制約を定義する前にaddSubviewを実行する

Visual Format Languageで制約を設定する場合、superviewに対して位置を設定したりするため、制約を定義する前にviewに追加しておく必要があります。

制約の定義

制約の定義には、addConstraintsを使います。

let views = ["wrapperView": wrapperView]

// 左右の位置をsuperviewとくっつける
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "|-0-[wrapperView]-0-|", 
    options: .AlignAllTop, 
    metrics: nil, 
    views: views))

// 上下の位置をsuperviewとくっつける
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "V:|-0-[wrapperView]-0-|", 
    options: .AlignAllTop, 
    metrics: nil, 
    views: views))

|-0-[wrapperView]-0-|この記述が、Visual Format Languageです。

|がsuperviewを表し、対象のviewは[]で囲みます。
V:という記述で縦の結びつきを表します。
viewsで渡したディクショナリから文字列の変数展開を行い、AutoLayoutが定義されます。

これで、画面いっぱいにwrapperViewが広がるようになりました。

f:id:w6500:20160219002834p:plain:w200

とりあえず今日はここまで。

【swift】対象のビューのAutoLayoutを計算した後のCGSizeを取得する

tableViewCellの高さ計算の時など、コード上で対象のViewのAutoLayoutを計算した後のサイズを取得したい時があります。

そんな時は、systemLayoutSizeFittingSizeを使います。

引数で渡す値に応じて、コンテンツを表示するための最小サイズか、最大サイズのCGSizeを返します。

サイズ計算時にはサブビューを含めたすべての制約が考慮されます。

// コンテンツを表示するための最小サイズを指定する
let minSize = view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

// コンテンツを表示するための最大サイズを指定する
let maxSize = view.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize)

【swift】【iOS】UIViewからUIImageを生成する

facebookに写真をシェアしたい時などに、アプリ側で組みて立てたUIViewからUIImageを生成したい時があります。

例えばアプリ側でこんなViewを組み立ててみます。

f:id:w6500:20151015000051p:plain

これはソースコードは雑につくるとこんな感じです。
5つのUIViewで組み立てられています。

let containerView = UIView(frame: CGRectMake(160, 200, 200, 200))

let view1 = UIView(frame: CGRectMake(0, 0, 50, 50))
view1.backgroundColor = UIColor.redColor()
containerView.addSubview(view1)

let view2 = UIView(frame: CGRectMake(50, 0, 50, 50))
view2.backgroundColor = UIColor.yellowColor()
containerView.addSubview(view2)

let view3 = UIView(frame: CGRectMake(0, 50, 50, 50))
view3.backgroundColor = UIColor.greenColor()
containerView.addSubview(view3)

let view4 = UIView(frame: CGRectMake(50, 50, 50, 50))
view4.backgroundColor = UIColor.blueColor()
containerView.addSubview(view4)

このcontainerViewからUIImageを生成します。

let image: UIImage?

// 第2引数は画像が不透明の場合はtrue
// 第3引数は倍率(scale)
UIGraphicsBeginImageContextWithOptions(containerView.frame.size, false, 1)

containerView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image = UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsEndImageContext()

これでUIImageの出来上がりなので、これを使用してシェアをするなり、UIImageViewにしちゃうなり、なんでもありです。

Tips

ここで生成したimageの見た目を確認する簡単な方法がxcodeには搭載されています。

imageが生成されたタイミングでブレークポイントを設定して処理を止めた後に、該当の変数にマウスオーバーすることで下記のような吹き出しが出てきます。

f:id:w6500:20151015001753p:plain

そして、この目玉みたいなアイコンをクリックすると、実際の画像がプレビューされるので、わざわざ画面に描画することなく生成した画像を確認することができます。

f:id:w6500:20151015001932p:plain

【iOS】【swift】カスタムViewとxibを紐付ける

swiftで独自のViewを定義した時に、見た目をxibで整える方法をまとめます。

1. 独自のViewクラスの定義

class CustomView: UIView {
}

2. 空のxibファイルを作成

f:id:w6500:20151013090133p:plain

3. xibで見た目を作成

f:id:w6500:20151013090421p:plain

4. File's Ownerで先ほどの独自Viewクラスを指定

f:id:w6500:20151013090707p:plain

これで、とりあえずxibファイルとViewクラスが紐付いたので、各要素のOutletやActionを定義できるようになります。

5. Viewクラス側でxibファイルを読み込み、xibファイルで定義した一番親のViewを追加する

File's Ownerでクラスを指定しただけでは、xibファイルを読み込むことはしません。(してくれればいいのに)

Viewクラス側で明示的に読み込んであげる必要があります。
xibファイルを読み込むにはloadNibNamedを呼び出します。

// xibファイル名を指定
NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil)

loadNibNamedを呼び出したとしても、まだカスタムViewには要素は何も入っていない状態です。(入ってくれればいいのに)

xibファイルの親のViewをaddSubviewで明示的に追加してあげる必要があります。

カスタムViewクラスの全体をまとめるとこんな感じになります。

class CustomView: UIView {

    // xibファイルで定義した一番親のView
    @IBOutlet var contentView: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)

        // xibファイル読み込み
        NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil)
       
        // Viewの大きさを定義
        bounds = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, 44)
        contentView.frame = bounds

        // xibファイルのViewをカスタムViewクラスに追加する
        addSubview(contentView)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

これで、このカスタムViewの見た目をxibファイルで整えられるようになります。

紐付けた後に、わざわざaddSubviewで追加しないといけないというのが、ちょっとイメージしづらいなと思いました。

【iOS】【Swift】convertRectを使ってframe位置を計算する親要素を変更する

要素の座標位置を取得するためには、その要素のframeを見ます。

view.frame.minX
view.frame.minY

この時の座標位置は親要素から見た時の相対的な位置になります。

例えばviewが3階層の構造になっている場合、それぞれの座標位置は下記のようになります。

  • 黄色いView: grandParentView
  • 赤いView: parentView
  • 青いView: childView

f:id:w6500:20150813194334p:plain

childViewはparentViewからの位置、parentViewはgrandParentViewからの位置になります。

この時、childViewの位置をgrandParentViewから計算して取得したい場合があります。

単純に考えれば、childViewの座標にparentViewの座標を足せば導き出せます。

parentView.frame.minX + childView.frame.minX
parentView.frame.minY + childView.frame.minY

ですがこれだと計算を自分で書くことになりバグを生みやすくなるので、かわりにconvertRectを使います。

let convertFrame = parentView.convertRect(childView.frame, toView: grandParentView)

convertRect:toViewは、第一引数のframeの位置をtoViewで渡した要素から計算して、新しいCGRectを返します。

これでchildViewの座標位置をgrandParentViewから計算して導き出すことができました。

f:id:w6500:20150813195628p:plain

【swift】Optional Bindingについて

前回swiftでのnilの取り扱いについてまとめました。

今回はnilを取り扱う時に便利な記法のOptional Bindingについてまとめます。

Optional Binding

必要になるケース

例えば、関数の中には返り値がOptionalなものがあります。
配列からindexの値を取得するfindなどがそうです。

var array: [String] = ["a", "b", "c"]
find(array, 'c') // 2
find(array, 'd') // nil

そこで、findの返り値を定数に代入してみます。

let index: NSNumber = find(array, 'd') // nil

これはコンパイルエラーになります。 定数indexはoptionalな定数ではないので、nilは代入できません。

let index: NSNumber? = find(array, 'd') // nil

Optionalにすればnilを代入できるようになるので、コンパイルが通るようになります。
次は、このindexの値をもとに配列から要素を削除してみます。

array.removeAtIndex(index)

これはコンパイルエラーになります。
indexはオプショナルな定数なので!をつける必要があります。

array.removeAtIndex(index!)

これは、コンパイルは通りますが、indexnilなので、ランタイムエラーが発生します。

つまり

indexの値がnilじゃ無いときに、配列から要素を削除したいのです。

これを、Optional Bindingを使用しないとこんな感じになると思います。

var array: [String] = ["a", "b", "c"]
let index: NSNumber? = find(array, "d")

if index != nil {
  array.removeAtIndex(index!)
}

いちいちindexを定義してnil判定するのはとても面倒です。

そこでOptional Bindingを使用します。

var array: [String] = ["a", "b", "c"]
if let index: NSNumber = find(array, "d") {
  array.removeAtIndex(index)
}

定数の定義とnil判定を同時に行うことができます。

ifのブロックの中は値がnilじゃない時に実行されるため、定数はoptionalではありません。
そのため、!も必要無くなります。

以上、Optional Bindingまとめでした。