ストーリーボードにナビゲーションコントローラ・タブバーコントローラを素早く追加するTips
ナビゲーションコントローラ・タブバーコントローラをStoryboardに追加するとき 右側のUtilityペインの Object Library からドラッグ&ドロップするとナビゲーションコントローラ・タブバーコントローラ以外にもテンプレートのビューコントローラが付いてきて、それらを削除してから使いたいビューコントローラにセグエを繋ぐとういことをしないとけいない。
- Utilityペインから Navigation Controller (Tab Bar Controller)をドラッグ&ドロップ
- 不要なテンプレートビューコントローラを削除
- ルートにしたいビューコントローラにセグエを繋げて完成[画像1]
これ面倒ですよね?!
ということで、使いたいビューコントローラを一気にナビゲーションコントローラ・ タブバーコントローラに 埋め込む(Embedする)方法 を紹介します。
Storyboard に UINavigationController を追加するTip
- ナビゲーションコントローラのルートにしたいビューコントローラを選択
- Xcodeのメニューの Editor を選択
- Embed In
- Navigation Controller を選択 (ヴューコントローラを選択していないと選択できない)
- 画像1が出来上がる
Storyboard に UITabBarController を追加するTip
タブバーコントローラの追加もナビゲーションの追加と同様に
- タブバーコントローラの子ヴューコントローラにしたいヴューコントローラを選択
- Xcodeのメニューの Editor を選択
- Embed In
- Tab Bar Controller を選択 (ヴューコントローラを選択していないと選択できない)
- 画像1のタブバーコントローラ版が出来上がる
swift の as as! as? オペレータ (Type Cast Operator) と is (Type Check Operator) とパターンマッチ
環境
as オペレータ
class Animal {} class Dog: Animal {} let dog = Dog() let animal = dog as Animal // アップキャスト let floatValue = 1 as Float // 型指定
as! オペレータ
- 強制的にダウンキャスト
- ダウンキャストが成功することが分かっている場合に使用
- ダウンキャストが失敗するとruntimeエラーになる
let animal1: Animal = Dog() let dog = animal1 as! Dog // OK let animal2 = Animal() let dog2 = animal as! Dog // ダウンキャストできないのでruntimeエラー
as? オペレータ
- ダウンキャストが成功するか分からない場合に使用
- 戻り値はオプショナル型
- 失敗した場合はOptional
.None
let animal1: Animal = Dog() let dog = animal1 as? Dog // dogの型はOptional<Dog> let animal2 = Animal() let dog2 = animal as? Dog // エラーは起こらない。dog2の中身は.None if let dog = dog2 { print("dog is not nil") } else { print("dog is nil") } // 出力: dog is nil
is 型チェックオペレータ
- isオペレータはインスタンスがある型のサブクラスの型か判定するときに使用
- 戻り値はある型のサブクラスの型のとき true, そうでないとき false
let animal = Animal() let isDog = animal is Dog print(isDog) // 出力: false let dog = Dog() let isAnimal = dog is Animal print(isAnimal) // 出力: true // AnyObject型にしないとコンパイラがSmartすぎて // 下の警告がでるためAnyObjectにしている: // 'is' test is always true let isDog2: AnyObject = dog is Dog print(isDog2) // 出力: true
as と is パターンマッチ (Type-Casting Patterns)
ややこしことに is, as はswitchのパターンマッチと一緒に使用できる。
class Animal { func bark() -> String { return "$&%#" } } class Dog: Animal { override func bark() -> String { return "woof-woof" } } let things: [Any] = [0, 0.0, Animal(), Dog()] for thing in things { switch thing { // thingがIntか判定。値自体に興味はないときに is を使用する case is Int: print("thing is Int value") case is Float: print("thing is Float") // thingがDogにマッチするか判定し // (asの右側のタイプかそのサブタイプか)、 // マッチした場合、Dogにキャストされたdogを取得 case let dog as Dog: print("Dog barks " + dog.bark()) case let animal as Animal: print("Animal barks " + animal.bark()) default: print("something else") } } /* 出力: thing is Int value thing is Float Animal barks $&%# Dog barks woof-woof */
Swiftで作ったアプリをリリースしました!!
土日を使って少しずつ作りました。 モチベーションが上がらないときはコワーキングスペースに行ってみたりして作業しましたが、 基本的には引きこもって作業。
Swift良いですね!
Swift 2.0 の indirect で2分探索木(Binary search tree)を試す
indirectが使用できる前はBoxクラスなんかを使って再帰的なenumデータ型 を実装していたみたいだが、indirectを使えばBoxクラス等を使わずにシンプルにList, Tree なんかを書ける。
環境
2分探索木
元ネタは すごいHaskellたのしく学ぼう! の第7章の再帰的なデータ構造
木
indirectで木を定義
indirect enum Tree<T> { case EmptyTree case Node(T, left: Tree<T>, right: Tree<T>) }
要素と2つの空部分木からなるノードを作る関数
func singleton<T>(a: T) -> Tree<T> { return .Node(a, left: .EmptyTree, right: .EmptyTree) }
2分探索木に要素を挿入する関数
func treeInsert<T: Comparable>(x: T, tree: Tree<T>) -> Tree<T> { switch tree { case .EmptyTree: return singleton(x) case let .Node(a, left: left, right: right): if x == a { return .Node(a, left: left, right: right) } else if x < a { return .Node(a, left: treeInsert(x, tree: left), right: right) } else { return .Node(a, left: left, right: treeInsert(x, tree: right)) } } }
Google Analytics for iOS with Swift
CocoaPodsを使用しないで、Google Analyticsを入れる方法
手動でGoogle Analyticsのスクリーン計測を行うまでをメモしておく。
環境
準備
Google Developers Download the SDKからからzipをダウンロードする。現時点でのバージョンは3.15
GoogleAnalytics/Library配下のファイルをプロジェクトに追加する
- GAI.h
- GAIDictionaryBuilder.h
- GAIEcommerceProduct.h
- GAIEcommerceProductAction.h
- GAIEcommercePromotion.h
- GAIFields.h
- GAILogger.h
- GAITrackedViewController.h
- GAITracker.h
Build PhasesのLink Binary With Librariesに以下を追加
Bridging headerを作成する(ex. BridgingHeader.h)
Build SettingsのObjective-C Bridging Headerに${PRODUCT_NAME}/BridgingHeader.hを記述
BridgingHeader.hに以下のインポート文を追加
// とりあえずLibrary配下のヘッダーをインポートする #import "GAI.h" #import "GAIDictionaryBuilder.h" #import "GAIEcommerceProduct.h" #import "GAIEcommerceProductAction.h" #import "GAIEcommercePromotion.h" #import "GAIFields.h" #import "GAILogger.h" #import "GAITrackedViewController.h" #import "GAITracker.h"
'GAI.h' file not found とBridgingHeaderでエラーがでるときは、Header Search Pathが設定されているか確認する。Build Settings -> Search Path - Header Search Paths 。設定されていない場合は、パスを追加 (例 $(SRCROOT)/** )。
※ #import <Google/Analytics.h>ができるのはPodからインストールしたときだけなのかな?
トラッキングIDを設定する
計測を行う前にトラッキングIDを設定する
GAI.sharedInstance().trackerWithTrackingId("UA-xxxxxx-x")
GoogleService-Info.plistを使用する場合は下でもOKなのかな?
var configureError:NSError? GGLContext.sharedInstance().configureWithError(&configureError) assert(configureError == nil, "Error configuring Google services: \(configureError)")
手動でスクリーン計測を行う(Automatic Screen Measurement)
var tracker = GAI.sharedInstance().defaultTracker tracker.set(kGAIScreenName, value: "Home Screen") // 計測するスクリーン名を記述する var params = GAIDictionaryBuilder.createScreenView().build() as [NSObject : AnyObject] tracker.send(params)
SwiftのRangeからArrayを取得する方法
環境
- swift 1.2
方法その1
mapを使う
let range = Range(start: 0, end: 10) let array = range.map{$0} println(array) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
方法その2
Array#initから生成する
let range = Range(start: 0, end: 10) let array = Array(range) println(array) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
カスタム画面遷移でモーダル表示をしたとき、dismissしたら画面が無くなっている!?
環境
現象
プログラムの内容:カスタム画面遷移でモーダル画面を表示させて、モーダル画面内のボタンが押されたら dismissViewControllerAnimated でカスタム画面遷移でモーダル画面を閉じる。
これをしたら、こんな感じです。
dismissを押して、モーダル画面を閉じたら、遷移後の画面に何も表示されていません。 なぜや!!
バグか?
いつも通りググったら、ここに辿り着きました。
I was having the same problem here – looks like a bug in iOS 8
iOS 8のバグらしい。 UIWindowのビュー階層が完全に空になっている。
Open Radarにもバグとして報告してあるようだ。
回避策
これを回避するには animateTransitionメソッドのアニメーション終了後に、 toViewController.view をkeyWindowにaddSubviewする。
今回のコード的には下のようにする。
@objc func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // From ViewController let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) // To ViewController let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) let fromView = fromVC!.view let toView = toVC!.view // アニメーションを実行するためのコンテナビューを取得 let containerView = transitionContext.containerView() containerView.insertSubview(toView, belowSubview: fromView) UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: .CurveLinear, animations: { fromView.transform = CGAffineTransformMakeScale(0.5, 0.5) }, completion: {(finished: Bool) in let completed = !transitionContext.transitionWasCancelled() transitionContext.completeTransition(completed) // これを追加!!! UIApplication.sharedApplication().keyWindow!.addSubview(toView) }) }
これで下のように表示されるようになりました。
今回のフルソースはこちらにあります。