UIPickerView の使い方

UIPickerView とはこういうやつです。

f:id:komaji504:20161212003154p:plain:w300

これを使うための最低限の部分です。

UITableView 等と同様に DataSource と Delegate を設定して、表示する列数、行数、要素を各メソッドで return するといった感じです。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var pickerView: UIPickerView! {
        didSet {
            pickerView.dataSource = self
            pickerView.delegate = self
        }
    }
    
    let components = (1...100).map { "\($0)" }
    
}

extension ViewController: UIPickerViewDataSource {
    
    // 列数
    // 1列であってもこのメソッドを省略できない
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    // 行数
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return components.count
    }
    
}

extension ViewController: UIPickerViewDelegate {

    // 要素
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return components[row]
    }
    
}

各要素の表示領域の backgroundColor を変更したりとか色々手を加えたい場合は、上記の Delegate メソッドではなく

func pickerView(UIPickerView, viewForRow: Int, forComponent: Int, reusing: UIView?)

UIPickerViewDelegate - UIKit | Apple Developer Documentation

を使って、色々手を加えた View を返すようにすることでできそうです。

ちなみに、UIDatePicker の countDownTimer の hour や min のように固定ラベルをつけたいとなっても、それようのプロパティやメソッドはなさそうでした。 やるなら UIPickerView とは別途、固定の UILabel を置く等しなければならなそうです。

UIAlertController 使ってみた

UIAlertController を使うことで Alert や Action Sheet を画面に表示することができます。 Alert や Action Sheet とはこういうやつ↓です。

Alert Action Sheet
f:id:komaji504:20161024011540p:plain f:id:komaji504:20161024011551p:plain

使う際の流れとしては、以下のような感じです。 Alert も Action Sheet もやることは同じなので Alert について書きます。

  1. Alert となる UIAlertController のインスタンスを生成する
  2. 表示する各項目 (キャンセルボタンとか) を作る
  3. 各項目をインスタンスに設定する
  4. 表示する ViewController で present する

Alert となる UIAlertController のインスタンスを生成する

インスタンス生成時にタイトルとメッセージと Action か Action Sheet かを設定します。

let alert = UIAlertController(title: "Alert",
                              message: "Alert Message",
                              preferredStyle: .alert // ここを .actionSheet に変えると Action Sheet になります
)

表示する各項目 (キャンセルボタンとか) を作る

項目の style (UIAlertActionStyle) は .default, .destructive, cancel の3つがあります。
.cancel 以外は複数指定できます。

let defaultAction = UIAlertAction(title: "Default",
                                  style: .default,
                                  handler: { action in
                                    print("Default")
})

let destructiveAction = UIAlertAction(title: "Destructive",
                                      style: .destructive,
                                      handler: { action in
                                        print("Destructive")
})

let cancelAction = UIAlertAction(title: "Cancel",
                                 style: .cancel,
                                 handler: { action in
                                    print("Cancel")
})

各項目をインスタンスに設定する

インスタンスに対して addAction(action: UIAlertAction) で設定できます。

alert.addAction(defaultAction)
alert.addAction(destructiveAction)
alert.addAction(cancelAction)

表示する ViewController で present する

現在表示されている ViewController で Alert を表示する場合は以下のように書きます。

present(alert, animated: true, completion: nil)

iPad 対応

iPad の場合、 Action Sheet はポップオーバーで表示されます。

Action Sheet for iPad
f:id:komaji504:20161024012758p:plain

この時、以下の設定がされていないとクラッシュするので忘れないよう気をつけてください。

// 表示先の View
actionSheet.popoverPresentationController?.sourceView = view
// 表示場所
actionSheet.popoverPresentationController?.sourceRect = rect

サンプル

こちらにサンプルを置いたので良かったら試してみてください。

github.com

shibuya.swift #5 で初めての LT をしてきた

shibuya.siwft で生まれて初めての LT をしてきました。

shibuya-swift.connpass.com

shibuya.swift にはお手伝いとして何度か参加させてもらっていたのですが、今回は自分が話す側だったのでとても緊張しました。頭が真っ白になることなく無事終わったので良かったです。

話すことになった経緯

会社の先輩に「なんか話してよ」と言われたので、「あ、はい、わかりました!(参加ボタンぽち)」という感じで LT をすることになりました。

自分は、今年配属されてからの6ヶ月間は Ruby on Rails でのAPI開発をやってきました。iOS開発をやるようになったのは7月からで、まだ3ヶ月しか経っていません。そのため、話す内容なんてない...とか思っていたのですが、勢いで参加ボタンを押してしまったので、何かネタを見つけなければと思いひたすら考えていました。

ペーペーの自分が技術的な話をするのはちょっと厳しいなと思っていたので、なんか経験談みたいのを話せればなあと思っていました。

だったら新卒の数ヶ月間で API開発とiOS開発両方やってきた人はあんまりいなそうだからそういうこと話せばいいのかあ、でも需要あるのかなあとか思っていたところ、「新卒でAPI開発からiOS開発やってきてどうだったかみたいな話が面白そう」という助言をいただいたので、やっぱりこれしかないと思い内容を練り始めました。

資料づくり

LT は見たことあるけどどうやって資料作れば良いかわからん!という感じだったので、まずは他人の発表資料を漁って、 LT の資料とはどんな感じかをつかむところから始めました。 資料だけだとちょっとよくわからんという感じもあったので、動画も見たりしました。

他にもLT 資料 作り方 みたいな感じでググったりしてみたりもしました。

まあ雰囲気は掴めた気がしたのですが、結局よくわからんとなったので、内容を練ることにしました。

まずは頭の中で、どんなことをやってきたか、どんなことを学んできたかを振り返ってみました。 ざっと、こういうことがあったなあという感じには振り返ることができ、なんとなくの話の流れをイメージすることができてので、次に、各話をもっと深堀りするためにマインドマップを書いてみました。

まあ書いてみたのですが、頭でざっと振り返った以上のことは書けずに特に進展がなかったので 、 実際にどんなことがあったのか具体的に思い出すために、 GitHub の PR や issue を眺めることにしました。

ネタ探しにはこれが一番効果がありました。この時に自分はどう考えて、どういうアドバイスをもらって、どういうことを学んだのかということを結構具体的に思い出すことができました。

そして、項目ごとに文章に書き出してみました。これを幾つか書いていると、ここの話を書こうとか、ここはやめておこうとか結構まとまってきたので、先輩に見てもらって、内容とか流れをブラッシュアップしていきました。

これを何回か繰り返して文章ではまとまったので、スライドを作り始めました。

まずスライドにはひたすら箇条書きで書いていきました。最初から最後までずらっと適当にスライドに書き起こしてから、一通りの流れを見て、それからスライドを分割したり、まとめたりを繰り返していきました。

最後にレイアウトやデザインを少しだけ修正して完成させました。

speakerdeck.com

練習

あまり緊張せずとも人前で話すことができる人もいるかと思いますが、自分はとても緊張してしまいます。

今回緊張してしまう理由は、大人数の・初対面の人に対して・LT をする ことに慣れていないことだと思います。常日頃当たり前にやっていることは緊張しないのと同様に、これにも慣れてしまえば緊張もしなくなると思います。

ただ、大人数という部分にすぐ慣れるなかなか難しいかと思います。初対面の人に対してという部分も同様です。ですが、LT をするという部分は、練習を重ねることで慣れることができます。そのために、何度も練習を繰り返しました。

練習は、以下の4つのフェーズに分けて行いました。

  1. 原稿を読みながら練習する
  2. 原稿をなるべく見ないようにして練習する
  3. スライドだけを見ながら練習する
  4. 何も見ずに練習する

これらをタイマーで時間をはかったり、音声を録音したりしながら行いました。

本番

とにかくやるだけ!!

やってみて

慣れていないことをするにはとても抵抗があって、最初の一歩がなかなか踏み出せないのですが、なんだかんだやってしまえばやれるもだなあと思いました。クオリティはどうであれ。

今回の LT も準備すればできるということが分かり、とても自信となったので、また機会があればやってみたいなあという気持ちになりました。

今後は、技術的な話にもチャレンジしていきたいです。

最後に、会場準備をしてくださった Fabric さん、ありがとうございました!

Swift の Class, Protocol を Objective-C で使う

Objective-C 側で ProjectName-Swift.h を import すれば使えるでしょとか思っていたのですが、すんなり使えなかったので書いておきます。

Class を使う

SampleObjcClass.h と SampleObjcClass.m からなる Objective-C のクラス内で、 Swift で書かれた SampleSwiftClass を使いたいとします。 この方法は、ヘッダファイル (.h) と インプリメンテーションファイル (.m) で違います。

インプリメンテーションファイルでは、ProjectName-Swift.h を import するだけで大丈夫です。

// SampleObjcClass.m

#import "ProjectName-Swift.h"

@implementation SampleClass

@end

ヘッダファイルの場合は、対象のクラスを class で宣言することで呼び出せるようになります。

// SampleObjcClass.h

@class SampleSwiftClass

@interface SampleObjectiveCClass

@end

継承する

Objective-C のクラスで Swift のクラスを継承することはできないようです。
そのため、 Swift のクラスを継承したいときは Objective-C のクラスを Swift に書き換える必要があります。何か別の方法あったら教えていただきたいです、、、

Protocol を使う

先ほど同様、 SampleObjcClass.h と SampleObjcClass.m からなる Objective-C のクラス内で、 Swift で書かれた SampleSwiftProtocol を使いたいとします。

この場合、 Swift 側で SampleSwiftProtocol の定義に @objc を付ける必要があります。

// SampleSwiftProtocol.swift

@objc protocol SampleSwiftProtocol {

}

Objective-C 側は、クラスの場合と同様にインプリメンテーションファイルでは、ProjectName-Swift.h を import するだけで大丈夫です。

// SampleObjcClass.m

#import "ProjectName-Swift.h"

@implementation SampleClass ()

@end

ヘッダファイルの場合は、対象のプロトコルを @protocol で宣言することで呼び出せるようになります。

// SampleObjcClass.h

@protocol SampleSwiftProtocol

@interface SampleObjectiveCClass

@end

適用する

適用する定義は、インプリメンテーションファイルにて記述する必要があるようです。ヘッダファイルではエラーとなってしまいます。

// SampleObjcClass.m

#import "ProjectName-Swift.h"

@implementation SampleClass () <SampleSwiftProtocol>

@end
// SampleObjcClass.h

@protocol SampleSwiftProtocol

// エラーになる
@interface SampleObjectiveCClass <SampleSwiftProtocol>

@end

参照

developer.apple.com

iOSDC Japan 2016 に参加してきた!

8/19, 20 にかけて開催された iOSDC Japan 2016 に参加してきました!
記念すべき第一回でした!
次回開催についてですが、 SNS やブログ等で拡散されていけばあるかもということでした。是非来年も開催されてほしいです!

iOSDC Japan 2016 の詳細はこちら↓

iosdc.jp

自分は iOS 開発を始めて期間が浅く、話を聞くのでいっぱいいっぱいで全然メモを取れませんでしたが、わずかに残したのでそれをちょろっと書きます。発表資料を公開されている方もいるので、そちらのリンクも貼っておきます。


AB Tests in Mobile App

kazunori kikuchi (@kichikuchi) さん

speakerdeck.com

iOS アプリ開発の補助ツールのベストプラクティス

宇佐見 公輔 (@usamik26) さん

speakerdeck.com

ライブラリ管理

Carthage とか

  • アプリとライブラリが分離される
  • バージョンが分かる

リソース取り込み

SwiftGen とか

コードチェック

SwiftLint とか

  • Warning を見逃さない
  • 量が多すぎて辛いならば、チェックをゆるくして、チェックを継続したほうが良い
  • そうすれば大事な Warning を見落とさない
  • SwiftLint の autoformat とかのコードフォーマットで自動変換できる

デザイナーにStoryboardをお任せする技術

Hiroki Kato (@cockscomb) さん

iosdc.jp

iOSアプリのリモートサポートツール「ミレタ」の作り方 #WebRTC #Swift #PrivatePod

Yuichiro Masui (@masuidrive) さん

iosdc.jp

Swift で JavaScript 始めませんか?

熊谷 友宏 (@es_kumagai) さん

www.slideshare.net

Xcode で快適なデバッグライフを追い求める

Toshihiro Morimoto @dealforest さん speakerdeck.com

クラッシュしたら AppDelegate だった

  • ExceptionBreakpoint を追加
    exception が発生したタイミングで breakpoint
    意図とは違うタイミングで break してしまうことが

  • Diagonostics を設定
    不正なメモリ操作を検知

どの ViewController かわからない

Symbolic Breakpoint with action を使って viewDidLoad と viewWillApear が呼ばれた時に break されるようにする

起動時に ViewController を指定したい

EnvironmentVariables で設定する

端末のログやファイルを取得したい

LLDB を使って slack に送信


全然メモ取っていなくてすみません。。。

constraint を動的に追加 / 削除する

AutoLayout でレイアウトに必要な constraint を追加する場合、 Storyboard 上で指定する方法と、コードで指定する方法があります。
Storyboard では静的な constraint であれば簡単に追加できますが、動的に追加、そして削除したい場合には、コードで指定しなければならないと思います。

以下では、対象の constraint はつくられているとして、その constraint を動的に追加 / 削除する方法を記述します ( iOS8 以上) 。

方法は、メソッドによる指定とプロパティによる指定の 2 通りがあります。

メソッドによる指定

これらのメソッドは、引数に [NSLayoutConstraint] をとるため、複数の constraint を 1 行で指定することができます。

let constraints = [hogeConstraint, fugaConstraint]

NSLayoutConstraint.activateConstraints(constraints) // constraint の追加

NSLayoutConstraint.deactivateConstraints(constraints) // constraint の削除

プロパティによる指定

単一の constraint を指定する場合にはこちらの方法で良いかと思います。
個人的にはこちらの方が読みやすくて好きです。

hogeConstraint.active = true // constraint の追加

hogeConstraint.active = false // constraint の削除

注意点

注意したいのは、 削除した constraint を再び追加する場合です。
具体的には、 Stroyboard 上で指定した constraint を IBOutlet で接続し、それを状況に応じて削除、そして再び追加するということをしたい場合です。

このとき、constraint の参照を weak として IBOutlet で接続してしまうと、削除したのちに再び追加しようとするとエラーになってしまいます。

@IBOutlet internal var hogeConstraint: NSLayoutConstraint! // weak 参照

hogeConstraint.active = false 

hogeConstraint.active = true // 実行時エラーになる

このエラーを解消するためには、 参照を strong にする必要があるようでした。

@IBOutlet internal var hogeConstraint: NSLayoutConstraint! // strong 参照

hogeConstraint.active = false 

hogeConstraint.active = true // エラーにならない

追記

strong にする必要があると書きましたが、強参照にすると循環参照の恐れがあるので、weak のままで true にする直前にフォースアンラップ等で nil チェックをする方が良さそうかなという気がしています。

参照

tomoyaonishi.hatenablog.jp

UITableView のセルの高さを可変にする

高さが可変の UILabel や UIImageView を UITableView のセルに配置したときには、もちろんセルの高さも可変にしたいと思います。

その方法を書きます。

ちなみに UILabel の行数を可変にする方法はこちらのエントリで書きました。

komaji504.hateblo.jp

セルの高さを可変にする

と言っても簡単で、以下のメソッドを実装してセルの見積もりの高さと、実際の高さを UITableViewAutomaticDimension で指定するだけです。

    func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 100 // セルの高さの見積もり
    }
    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

UITableView は estimatedRowHeight, rowHeight プロパティを持っているので、上記のメソッドを実装せずに、以下のように viewDidLoad() 等で直接指定しても大丈夫です。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.estimatedRowHeight = 100  // セルの高さの見積もり
        tableView.rowHeight = UITableViewAutomaticDimension
    }

見積もりの高さ

UITableView では UILabel 等の高さからセルの高さを計算させる必要があるのですが、計算処理が複雑な場合に負荷が大きくなってしまいます。
そこで、見積もりの高さを指定することで、 実際にセルを描画するタイミングまで見積もりの高さを使って計算させ、実際の計算を遅延させることができます。

そのため、この高さの指定は大体で構いません。

注意点

estimatedRowHeight の値を CGFloat.min で指定すると、 iPhone5 等の 32ビットCPU のデバイスではクラッシュしてしまいます。
また、 64ビットCPU のデバイスでも、描写されたセルの個数が指定した数でなかったりすることがあるようなので、 CGFloat.min で指定せずに、マジックナンバーで指定するのがよさそうです。