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を指定します。

let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
let items = Observable.just([
    SectionModel(model: "First section", items: [
            "aaaa",
            "bbbb",
        ]),
    SectionModel(model: "Second section", items: [
            "cccc",
            "dddd",
        ])
    ])


セルの生成

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


データとTablewViewをBindする

items
    .bindTo(tableView.rx_itemsWithDataSource(dataSource))
    .addDisposableTo(disposeBag)


ヘッダーのカスタマイズ

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

2016年4月16日土曜日

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

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


let disposeBag = DisposeBag()
var account:Variable = Variable("")

// 参照方法
account.value



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


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

2016年4月15日金曜日

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

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

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

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

    viewModel?.items
        .asDriver()
        .drive(tableView.rx_itemsWithCellIdentifier("CustomTableViewCell",cellType: CustomTableViewCell.self)) { row, item, cell in
            cell.item = item
            cell.tag = row
        }
        .addDisposableTo(disposeBag)


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

EditingStyleのセット

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

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

2016年4月12日火曜日

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

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


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

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


解決策

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


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


weak追加
 weak var delegate: ViewModelDelegate?

2016年4月3日日曜日

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

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

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で変更可能です。

@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
    }

}