プログラマ英語学習日記

プログラミングと英語学習のまとめなど

WWDC Introducing Drag and Drop まとめ

今回はiOS11の目玉といえるドラッグ&ドロップ機能のセッションです。

他にもドラッグ&ドロップはセッションが多いので、Appleの気合のいれようが伝わってきますね。

とりあえずIntro部分のセッションを。

Introducing Drag and Drop

ビデオは こちら

Drag&Dropのコンセプト

  • Drag&dropとは?

    • タッチによるデータの移動・コピー
  • Drag&Dropのゴール

    • 素早い応答。非同期取得
    • セキュリティ。アクセス制限など
    • マルチタッチで直感的に操作できる
  • 基本的にiPad向けの機能

    • iPhoneでは同一アプリ内に制限される

API概要

  • 4つのフェイズがある
    • Lift : 長押しでドラッグ可能になった状態
    • Drag : プレビューが移動してる状態。
    • SetDown : キャンセル、もしくは転送先の決定
    • DataTransfer : ターゲットへのデータ転送

Drag元のAPI

  • DragInteraction 系APIが追加
    • UIDragInteraction, UIDragInteractionDelegate
    • UIDragItem
let view : UIView = ...
let delegate : UIDragInteractionDelegate = ...
let dragInteraction = UIDragInteraction(delegate : delegate)
view.addInteraction(dragInteraction)
  • Delegateの役割

    • Lift時に、UIDragItem を返す
    • データが無い場合はDragフェイズ終了。
  • UIDragItemとは?

    • 以下の2つを持つ
      • Dragプレビュー
      • NSItemProvider

Drop先のAPI

  • UIPasteConfiguration が追加
    • 対応するデータ種別を指定する
    • Drop対象のViewに追加する
let config = UIPasteConfiguration(typeIdentifiersForAcceptingClass : NSStrnig.self)
view.pasteConfiguration = config

//UIViewを継承して作る? UIResponderのメソッドだった
override func paste(itemProiders: [NSItemProvider]) {
}
  • UIDropInteractinoDelegate で受け取りを行う
    • 受け取り可能かどうかを指定するのもこのDelegate

フェーズ詳細

Lift

  • ロングプレスで移動可能になった状態
    • ドラッグ待ち
    • Delegateで「移動するデータとプレビュー用View」を返す
//アイテムの決定
func dragInteraction(_ interaction: UIDragInteraction, 
                     itemsForBeginning session: UIDragSession) -> [UIDragItem] {
    let itemProvider = NSItemProvider(object: "Hello World" as NSString)
    let dragItem = UIDragItem(itemProvider: itemProvider)
    return [ dragItem ]
}
//プレビュー
func dragInteraction(_ interaction:UIDragInteraction,
          previewForLifting item:UIDragItem,
          session:UIDragSession) -> UITargetedDragPreview? {
  let imageView = UIImageView(image: UIImage(named: "MyDragImage"))
  let dragView = interaction.view!
  let dragPoint = session.location(in: dragView)
  let target = UIDragPreviewTarget(container: dragView, center: dragPoint)
  
  return UITargetedDragPreview(view: imageView,
       parameters:UIDragPreviewParameters(),
       target)
}
//リフトアニメーション
func dragInteraction(_ interaction: UIDragInteraction,
              willAnimateLiftWith animator: UIDragAnimating,
              session: UIDragSession) {
  animator.addAnimations {
    self.view.backgroundColor = UIColor.gray
  }
  animator.addCompletion { position in
    if position == .end {
      //リフト完了
    } else if position == .start {
      //リフトキャンセル。元に戻る
    }
  }
}

Drag 送信

  • ドラッグが成功したかどうかに応じてDelegateを実装する
func dragInteraction(_ interaction: UIDragInteraction,
      previewForCancelling item: UIDragItem,
      withDefault defaultPreview: UITargetedDragPreview) -> UITargetedDragPreview?

func dragInteraction(_ interaction: UIDragInteraction,
      item: UIDragItem,
      willAnimateCancelWith animator: UIDragAnimating)

func dragInteraction(_ interaction: UIDragInteraction,
      session: UIDragSession,
      didEndWith operation: UIDropOperation)

Drag 受信

  • 移動先が「データを受け取れるか」を返す
    • DropInteractionDelegate
//sessionをHandleできるかどうか
func dropInteraction(_ interaction: UIDropInteraction,
        canHandle session: UIDropSession) -> Bool {
    return session.canLoadObjects(ofClass: UIImage.self)
}
//handle可の後、Viewに入った時に処理可能かどうか
func dropInteraction(_ interaction: UIDropInteraction, 
                  sessionDidUpdate session: UIDropSession) -> UIDropProposal {
    return UIDropProposal(operation: UIDropOperation)
}
//
func dropInteraction(_ interaction: UIDropInteraction, sessionDidExit session: UIDropSession)
  • DropProposalは4種類ある

    • cancel
    • copy
    • move
      • これは同一アプリ内限定
        • 移動するように作り手があわせる必要がある
      • 送信元が「move可能」とする必要がある
    • forbidden
      • Cancelににてるが、特殊なバッジがつく
      • 利用例: ReadOnlyなフォルダに移動させようとした場合
  • SpringLoading(レインボーカーソルと訳すべきか?)

    • UIButtonに追加
let button = UIButton()
button.isSpringLoaded = true
let springLoadedInteraction = UISpringLoadedInteraction { (interaction, context) in
  //長時間の処理
}
view.addInteraction(springLoadedInteraction)

Drop

  • drop後は dropInteraction が呼び出される
    • データを読み込む場所
func dropInteraction(_ interaction: UIDropInteraction,
                     performDrop session: UIDropSession) {
    session.loadObjects(ofClass: UIImage.self) { objects in
        for image in objects as! [UIImage] {
           self.imageView.image = image
        }
    }
}

デモ

  • 動画29分からコードを交えながらのデモ

    • コルクボードに写真をおくサンプル
    • 気になった部分のみメモ
  • 同一アプリ判定

    • DropSessionで同一アプリ内か外部アプリかで処理分岐が必要になる
    • アプリの仕様によりますけど
//同一アプリ内なら移動、別アプリからならコピー
func dropInteraction(..., sessionDidUpdate ...) -> UIDropProposal {
  let operation: UIDropOperation
  if session.localDragSession == nil {
    operation = .copy
  } else {
    operation = .move
  }
  return UIDropProposal(operation: operation)
}

//Drop後
func dropInteraction(..., performDrop ...) {
  if session.localDragSession == nil {
    //コピー
    dropPoint = session.location(in: interaction.view)
    for dragAtime in session.items {
      loadImage(dragItem.itemProvider, center: dropPoint)
    }
  } else {
    //ボード内での移動、データコピーは不要
  }
}

//Dropアニメーション
func dropInteraction(_ ...   previewForDropping: ...) {
   if session.localDragSession == nil {
     //外部ならなにもしない
   } else {
     //内部なら移動処理
     return defaultPreview.retargetedPreview(...)
   }
}

WWDC What's New In Swift まとめ

早速elmのほうがネタ切れになってきました…やると分かるんですが、 一線を超えると本当に簡単な言語 であまり紹介するTipsがないんですよね。

組み方もいわゆるReduxが強制されますし、Middlewareもフレームワーク内蔵の方法で完結するので、組み方の選択肢があまりないというのも一因です。

ですのでちょっと趣向を変えてネイティブアプリの紹介をしていきたいと思います。

当面のテーマは iOS

WWDCの気になるセッションの概要を書いていきたいと思います。

続きを読む

肥大化するModelTreeとどう戦うか(が分かっていない)

だんだんelmのサンプルが大きくなってきました。Natigationも導入し、いわゆるSPA化してます。

ここで問題になってきたのが、「どう巨大なTreeを管理するか」

言語の難しさの壁を越えたら次は他言語でもあるあるな問題に遭遇しました。

続きを読む

関数型よく知らない人のelm失敗編(2) - module

つい先ほど、モジュールの「階層化」について知りました…厳密にはやり方が分かったといったほうが的確かも。

そりゃ普通に考えれば当然のことなので、あまり役に立たない気はしますが備忘録かねてまとめておきます

続きを読む

関数型よく知らない人のelm入門 番外編 - 失敗した設計たち

elmを触り始めて2週間ほど、かなりスムーズに組めるようになってきました。

規模が1段階あがるごとに「この組み方じゃダメだ!」となり組み直しているので絶対的なスピードは遅いですけど。

約30ファイル、2000行を超えてきたのでちょっと自分なりに把握したコツ、および失敗例をまとめていきます。

※まだ経験浅いので「ベストプラクティス」というには程遠いかと。

※便宜上、オブジェクト指向の用語を多少用います。

続きを読む

関数型よく知らない人のelm入門(5) - Jsonパース実践サンプル集

フロントエンドのコードを作成するとき、誰もが遭遇するであろう Jsonパース について今回はとりあげたいと思います。

これがなれるまでつらい。

Java等の言語が「Jsonオブジェクトから要素をとりだしていく」のに対し、elmでは「パースロジックを組み上げいく」というフローで実態が見えにくく、理解を困難にしています。

特に「途中経過をログ出力させる」のが難しい(面倒)。パースをミスしたときのエラー原因も分かりづらいです。

そこで今回はまずは組めるようになるという方針でいきます。

題して、JSONパース実践サンプル集

細かい理論は後回し、習うより慣れろ、理解はできた後にやってくるのコンセプトの元、とにかくサンプルコードを書いてみます。

ぜひオンラインエディタで色々編集しながら試してください。

続きを読む

関数型よく知らない人のelm入門(4) - 引数の順序

今回は自分なりにつかんだ elm のコツでも。コツというよりも「オブジェクト指向からの頭の切り替え方」とでもいいましょうか。

最初は引数の順番わけわからん!という状態でしたが、組んでいくうちに「ルール」みたいなものをちょっとずつ掴めてきました。

正しいとはいい切れないかもしれませんが、多少参考になれば。(たぶんですけど関数型全般に共通する要因かと思います)

続きを読む