2016年4月28日木曜日

[RxSwift] SectionありのDataSourceを生成する

本家のサンプルにも記載されている通り、RxDataSourcesを使用する方法があります。
https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxDataSources

RxDataSources:
https://github.com/RxSwiftCommunity/RxDataSources

Podfileに記載するなりしてインストールしてください。

SectionありのdataSourceを生成する

RxTableViewSectionedReloadDataSourceを使用します。SectionModelでSectionに表示するelementを指定します。

  1. let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
  2. let items = Observable.just([
  3. SectionModel(model: "First section", items: [
  4. "aaaa",
  5. "bbbb",
  6. ]),
  7. SectionModel(model: "Second section", items: [
  8. "cccc",
  9. "dddd",
  10. ])
  11. ])


セルの生成

以下のように、indexPathとelementが引数に入っているのでコールバック内でセルを生成する。
  1. dataSource.configureCell = { (_, tableView, indexPath, element) in
  2. let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
  3. cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
  4. return cell
  5. }


データとTablewViewをBindする

  1. items
  2. .bindTo(tableView.rx_itemsWithDataSource(dataSource))
  3. .addDisposableTo(disposeBag)


ヘッダーのカスタマイズ

UITableViewDelegateで処理する必要があります。
以下のように、ヘッダー用のViewとHightを返すモジュールを実装します。
  1. func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  2. let view = UITableViewHeaderFooterView()
  3. view.textLabel?.text = dataSource.sectionAtIndex(section).model ?? ""
  4. return view
  5. }
  6. func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  7. return 44
  8. }

2016年4月16日土曜日

[RxSwift] UITextFieldの入力テキストと通知

ViewModel側にObserverを作成する。ObserverでText更新を受け取ってViewModelの処理を行う。


  1. let disposeBag = DisposeBag()
  2. var account:Variable = Variable("")
  3. // 参照方法
  4. account.value


UITextFieldのrx_textとViewModelのObserverをBindする。
Textが更新されると、ViewModel側のObserverまで届く。
  1. let disposeBag = DisposeBag()
  2. accountTextField.rx_text
  3. .bindTo(viewModel.accountSubject.asObserver())
  4. .addDisposableTo(disposeBag)


プログラム上でTextを更新する場合(ストリームに流す場合)、sendActionsForControlEventsでValueChangedイベントをUITextFieldに流し込む。
  1. accountTextField.text = "test"
  2. accountTextField.sendActionsForControlEvents(.ValueChanged)

2016年4月15日金曜日

[RxSwift] UITableViewでカスタムセルを使用する

UITableViewで表示するデータをVariableで宣言するし、データの更新はvalueにセットする。

  1. let items = Variable<[Item]>([])
  2. // 新しいデータで更新
  3. self.items.value = updateItems

UITableViewにデータをBindingするには、rx_itemsWithCellIdentifierをコールする。
カスタムセルを使用する場合は、第二引数のcellTypeにセットが必要です。

  1. viewModel?.items
  2. .asDriver()
  3. .drive(tableView.rx_itemsWithCellIdentifier("CustomTableViewCell",cellType: CustomTableViewCell.self)) { row, item, cell in
  4. cell.item = item
  5. cell.tag = row
  6. }
  7. .addDisposableTo(disposeBag)


セルの選択はrx_itemSelectedを使用します。
  1. tableView.rx_itemSelected
  2. .subscribeNext { [unowned self](indexPath) in
  3. self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
  4. // get cell
  5. let cell: CustomTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)! as! CustomTableViewCell
  6. }
  7. .addDisposableTo(disposeBag)

EditingStyleのセット

rx_itemsWithCellIdentifierを使用すると、DefaultでEditting可能になってしまいます。
不要の場合はUITableViewCellEditingStyle.Noneをセットしましょう。

  1. func tableView(tableView: UITableView, editingStyleForRowAtIndexPath: NSIndexPath) -> UITableViewCellEditingStyle {
  2. return UITableViewCellEditingStyle.None
  3. }

2016年4月12日火曜日

[Swift]循環参照(相互参照)によるメモリリーク

MVVM移行時にクラス分けを行った結果、ViewControllerのメモリリークが発生しました。deinitが呼ばれず期待動作となりませんでした。


循環参照(相互参照)によるメモリリーク

以下のような状態になると、メモリリークが発生します。
何も考えずにViewModelのDelegateを追加、DelegateにViewControllerをセットでメモリリーク発生


解決策

弱参照になるようにDelegate用プロトコルをclass継承し、weakで保持できるように対応。


protocolにclass継承追加
1
2
public protocol ViewModelDelegate : class{
}


weak追加
1
weak var delegate: ViewModelDelegate?

2016年4月3日日曜日

[Swift]UIButtonでPressed状態の装飾を行う

AndroidならSelectorのxmlファイルを作成すればボタンの状態に合わせた装飾できます。
Swiftではボタンの文字などは次のfuncで設定可能です。

1
2
3
4
func setTitle(_ title: String?,forState state: UIControlState)
func setTitleColor(_ color: UIColor?,forState state: UIControlState)
func setTitleShadowColor(_ color: UIColor?,forState state: UIControlState)
func setBackgroundImage(_ image: UIImage?,forState state: UIControlState)

layerなどで装飾をする場合は、次のようにhighlightedのdidSetで変更可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@IBDesignable class MyUIButton: UIButton {
     
    @IBInspectable var borderColor :  UIColor = UIColor.blackColor()
    @IBInspectable var borderHighLightedColor :  UIColor = UIColor.clearColor()
     
    override internal func awakeFromNib() {
        super.awakeFromNib()
    }
     
    override var highlighted: Bool{
        didSet{
            if (highlighted) {
                self.layer.borderColor = borderHighLightedColor.CGColor
            } else {
                self.layer.borderColor = borderColor.CGColor
            }
        }
    }
    // Attributes Inspectorで設定した値を反映
    override func drawRect(rect: CGRect) {
        self.layer.borderColor = borderColor.CGColor
    }
 
}