以下のように、ボタンをタップしてビューを追加し、それをドラッグして動かせるようにします。
実装方法
以下のように役割を分けて実装します。
-
ViewController
: ビューを追加するボタンを提供する -
MainView
: ビューをドラッグして移動させる処理を行う
ViewController
ViewControllerでは、ツールバーの設置とボタンタップ時にビューを追加する処理を実装します。
StoryBoardにて、このViewControllerのルートビューの型をMainView
に設定しています。
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
private func setup() {
navigationController?.isNavigationBarHidden = true
navigationController?.isToolbarHidden = false
let addButton = UIBarButtonItem(
barButtonSystemItem: .add,
target: self,
action: #selector(addView))
toolbarItems = [
.init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
addButton,
.init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)]
}
@objc func addView() {
let v = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
v.backgroundColor = .black
let mainView = view as! MainView
mainView.addSubview(v)
}
}
MainView
ビューをドラッグして移動させる処理はこちらのクラスで実装しています。
import UIKit
class MainView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
backgroundColor = .lightGray
}
override func addSubview(_ view: UIView) {
super.addSubview(view)
view.center = center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// [1]
guard let touch = touches.first, let view = touch.view else { return }
// [2]
if view != self {
view.center = touch.location(in: self)
}
}
}
メインとなる実装はtouchesMoved(_:with:)
メソッドです。
このメソッドはMainView
上にあるビューをタッチしたまま動かしたときに呼ばれます。
[1]
タッチ操作に関する情報を保持するUITouchクラスのインスタンスを取得します。
デフォルトではインスタンスは1つだけ格納されています。
isMultipleTouchEnabled
をtrue
にすると複数タッチに対応し、インスタンスが複数格納されます。
さらに、タッチが検出されたビューをview
プロパティから取得しています。
[2]
タッチが検出されたビューが自分自身でないことを確認します。
ドラッグして動かしたいのはサブビューであるためです。
location
プロパティから現在のドラッグ位置を取得することができます。
ビューの位置をこのドラッグ位置に合わせることで、指のドラッグに合わせてビューを動かすことができます。
まとめ
ドラッグしてビューを動かす実装について紹介しました。
今回はtouchesMoved(_:with:)
だけを実装しましたが、本来であれば同じタッチ関連のメソッドであるtouchesBegan(_:with:)
等の実装も合わせて行う必要があります。
これはレスポンダーチェインに関連した話ですが、今回の記事の範囲からは離れるのでここでは取り上げません。
詳細は公式ドキュメントをご確認ください。