DRYな備忘録

Don't Repeat Yourself.

【iOS】StackViewで子供のViewを3分の1幅にしたい【AutoLayout】

ゴール

  • StackView(やAutoLayout)を使っていて、1/3分割にしたいときがある
  • Multiplierに整数や小数は入れたことあるけど、分数(無限分数)はどうやって入れるんだ?

f:id:otiai10:20160925025835p:plain

調査

stackoverflow.com

解決

結局、CGFloatへのalias表現みたいなものなので、3つとも「1:3」を指定してしまうと警告が出る。なので、たとえば真ん中は指定せず(Super ViewとしてStackViewを使っているなら「残りの幅をうめる」)とするのが良いっぽい。

雑感

Fabric/Crashlyticsで同プロジェクトの別bundle identifierのアプリを追加する

twittercommunity.com

  1. Xcodeにおいてbundle identifierを変更する
  2. Fabricデスクトップアプリにおいて「+ New App」というボタンがあるのでクリック
  3. 同プロジェクトを選択する
  4. チュートリアルが始まるが、だいたいのことは済んでるので、⌘+Bとか適当にしてチュートリアルをこなす
  5. おわり

【iOS】画面の向き(Orientation)を特定のページのみで制限したり許可したりしたい【supportedInterfaceOrientations】

ゴール

たとえば

  • 基本的にPortrait(縦向き)のみに制限したいんだけど、特定の画面だけではLandscape(横向き)を許可したい

特定のViewで、強制的に向きを変えることはできる

けど、これは向きを変えるだけであって、ふたたび端末をぐるっとすると縦向きに戻ったりする。かなしい。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Force Orientation Landscape
        UIDevice.currentDevice().setValue(UIInterfaceOrientation.LandscapeLeft.rawValue, forKey: "orientation")
    }

Info.plistで、全体的に使うOrientationは制限できる

プロジェクトファイル(file path的にはInfo.plist)で、以下のような項目があるから「これでいーじゃーん」と思ってチェックしても、これはアプリケーション全体で許可非許可の扱いなので、「◯◯というViewだけは横ね」みたいにホワイトリスト的な挙動は設定できない。

f:id:otiai10:20160919045040p:plain

supportedInterfaceOrientationsを使う

ViewControllerのメソッド(shouldAutoRotateと)supportedInterfaceOrientationを使えば、ViewControllerごとにサポートするOrientationを明示的に指定できる。

class MyViewController: UIViewController {

    // ...

    // ここでtrueを返すと、`supportedInterfaceOrientations`が呼ばれる
    override func shouldAutorotate() -> Bool {
        // まあでもデフォルトでtrue返すっぽいし、overrideしなくていいっぽい
        return true
    }

    // ここでサポートできるOrientationを明示できる
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

supportedInterfaceOrientationsがなんかうまく動かないときに疑うこと

しかし、これだけだと(だいたいの場合)動かない(と思う)。なぜなら、

When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. supportedInterfaceOrientations #Discussion

「ユーザがデバイスの向きを変えたとき、システムはルートのViewControllerもしくは一番上で画面いっぱいに表示されているViewControllerの supportedInterfaceOrientationsを呼びます。」

とのことで、だいたいの場合、UINavigationControllerとか、TabBarControllerとか、PageViewControllerとかにこれを実装するハメになってくるんではないかと思う。

UINavigationControllerを継承したMyNavigationControllerとかを作ってもいいんですけど

一案として、上記のメソッドを実装した独自のMyNavigationControllerを作って、下記のようにするのかもしれない。

class MyNavigationController: UINavigationController {
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}
// で、Storyboardなりで、起点になるNavigationControllerの
// classをこいつに指定してあげれば、このメソッドが呼ばれるはず

たぶんこれでも動くんだけど、若干のだるさがあるので、標準のUINavigationControllerのextension作っちゃえばいいじゃないか、という結論になった。

最終的に

// TabBarControllerのcontextがメイン
extension UITabBarController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    // もうちょっとちゃんとやる場合は、子供のViewControllerに
    // supportedInterfaceOrientationsを実装して
    // return selectedVC.supportedInterfaceOrientations()
    // とかすれば、判断を子供に委譲できる
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        guard let selectedVC = self.selectedViewController else {
            return UIInterfaceOrientationMask.Portrait
        }
        // TabBarControllerの中でNavigationを使ってるケースがある
        guard let navigation = selectedVC as? UINavigationController else {
            return UIInterfaceOrientationMask.Portrait
        }
        // Navigationで一番うえのやつを取得して
        guard let current = navigation.viewControllers.last else {
            return UIInterfaceOrientationMask.Portrait
        }
        // これ!このビューのときだけ横を許可して!!
        if current is MyCoolViewController {
            return UIInterfaceOrientationMask.Landscape
        }
        return UIInterfaceOrientationMask.Portrait
    }
}

// サービスの登録フローとかは、いきなりNavigationが出るので
extension UINavigationController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

// サービスのイントロ画面は、いきなりPageViewControllerなので
extension UIPageViewController {
    override public func shouldAutorotate() -> Bool {
        return true
    }
    override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

という感じになった。これを、AppDelegate.swiftのケツにでも追加しとく。今回は明示的に "ここでだけで" まとめたかったので、各々のsupportedInterfaceOrientationを呼ぶ実装にはしなかったけど、必ず各々に判断を委譲するほうが綺麗なケースもあると思う。

雑感

  • swiftのコードをインターネッツでググっても、動いたり動かなかったりする
  • swiftという言語が若いからかなと思ったけど、どうやらそうでもない
    • 情報の絶対量はAndroidよりも多いし
    • Objective-Cの情報でも参考にできることは多いし
  • おそらくアプリの要件が人それぞれで、しかもそれがけっこう切り分けられないんだと思われる
    • 今回のケースでいうとroot view controllerがTabBarControllerだったり、NavigationControllerだったり、PageViewControllerだったりした
    • とかそういうこと
  • であるからして、慌てず焦らず、ちゃんと英語読んで、公式も当たって「なぜ動かないか」を環境非依存な知識までかみ砕いて理解していくのが大事だなあ、と思った
  • 勉強になります

DRYな備忘録として

【Go言語】ローカルのGoの(継続的な)バージョンアップ【go1.7】

なんかいつの間にこんなrepoあったの

git clone https://go.googlesource.com/go

とりあえず現状確認

% go version
go version go1.6.2 darwin/amd64
% echo $GOROOT
/Users/otiai10/.go/1.6.2
% ls /Users/otiai10/.go
1.4.3 1.5.3 1.6.2 

なるほど、かつての自分はディレクトリを分けて保存していたっぽい。ミラーじゃなくて公式にソースを配布するレポジトリになってるので、これからはgitのrevisionでソースのバージョンを管理できるはず。ということで、以下のような構成にしました。

% cd /Users/otiai10/.go
% git clone https://go.googlesource.com/go tip
% ls /Users/otiai10/.go
1.4.3 1.5.3 1.6.2 tip

いいかんじ。そして、いざインストール。これ Installing Go from source - The Go Programming Language に従って、

% cd /Users/otiai10/.go/tip
% git checkout go1.7 # "go1.7" のリリースタグのリビジョンへ移動
% cd src
% GOROOT_BOOTSTRAP=/Users/otiai10/.go/1.6.2 ./all.bash
# 1.4以降のバイナリを要求するので、そこへのpathを指定する
# 今回は、直前の1.6を使った。今後も1.6でいいだろうと思う

で、

# 中略...
+pkg debug/pe, type Section struct, Relocs []Reloc
+pkg debug/pe, type StringTable []uint8
+pkg time, func Until(Time) Duration

ALL TESTS PASSED

---
Installed Go for darwin/amd64 in /Users/otiai10/.go/tip
Installed commands in /Users/otiai10/.go/tip/bin
*** You need to add /Users/otiai10/.go/tip/bin to your PATH.

と言われるので、~/.zshrcGOROOTの値を/Users/otiai10/.go/tipに変え、PATH=$PATH:$GOROOT/bin:$GOPATH/binは変わらず。別シェルを立ててログインしなおすと、

% go version
go version go1.7 darwin/amd64

良い感じっぽい。エディタの環境設定もちょっといじる必要があるはず。まあ後でいいや。そんで、今後1.8、1.9とかがリリースされたら、

% cd /Users/otiai10/.go/tip
% git fetch origin
% git checkout {なんかそのバージョン}
% cd src
% GOROOT_BOOTSTRAP=/Users/otiai10/.go/1.6.2 ./all.bash

とすればいいはず。PATHはtipに通ってるので、もう編集する必要は無いはず。

雑感

  • ベルリンで働きはじめて半年が経ちました
  • サーバサイドエンジニアのはずが、iOS書いてます
  • ビール🍺の飲み過ぎで太りました

DRYな備忘録として

f:id:otiai10:20160904071402j:plain

SwfitでTableViewをつかってかっこいいフィードを実装するときに習得したことまとめ

いかんせんスケジュールがギリギリだったので、個別にエントリ書くのは無理でした

f:id:otiai10:20160829070135p:plain

なにもしてないのにXcodeがこわれた: An error was encountered while running (Domain = LaunchServicesError, Code = 0)

なおった

rm -rf myproject
git clone git@github.com:otiai10/myproject.git
open myproject.xcworkspace

rm -rf 最強説