UIViewやUIWindowをドラッグで移動したりサイズ変える方法


### イメージ

こういうことがしたい人。

![UIView_ChangeSizeAndMoveWindow.gif](https://qiita-image-store.s3.amazonaws.com/0/37274/bd633a1d-4b38-8a8c-b097-9fa74002903b.gif)


### 解決方法

- タップの検知
- 移動かサイズ変更か判定
- 移動量の反映


### 使用メソッド

~~~swift
UIView.touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
UIView.touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
UIView.touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
UITouch.locationInView(UIView?) -> UIPoint
CGRect.offsetBy(dx dx: CGFloat, dy: CGFloat) -> CGRect
~~~

### 何をやってるか

- タッチ開始時
  - タッチ場所から位置移動なのかサイズ変更判定
  - タッチ位置を初期タッチ位置を対象ビューの相対位置として記憶
- タッチ移動時
  - サイズ変更の場合
    - UIView.frame のサイズ部分のみを変更
  - 位置移動の場合
    - タップ位置と初期相対位置を UIView.frame に反映


#### なぜ初期タッチ位置が必要なのか？
これを外して移動させると、タップ位置にビューの左上(Origin)が来てしまい、操作性が悪くなる。
サイズ変更はこれをしてないため、タップ位置にビューの右下(Origin+Size)がきてる。



### コード全文

~~疲れたので~~ まとめとして下記にコード全文

~~~swift
class MyView : UIView
{
    enum BehaviorMode : Int{
        case None
        case MoveWindowPosition
        case ChangeWindowSize
    }
    
    private var behaviorMode:BehaviorMode = .None
    
    private var locationInitialTouch:CGPoint!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 0.7)
        self.userInteractionEnabled = true
        self.windowLevel = UIWindowLevelAlert
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.locationInView(self)
            print("Began:(\(location.x), \(location.y))")
            locationInitialTouch = location
            
            if location.x > bounds.width - 20 && location.y > bounds.height - 20{
                behaviorMode = .ChangeWindowSize
            }else{
                behaviorMode = .MoveWindowPosition
            }
        }
    }
    
    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.locationInView(self)
            print("Moved:(\(location.x), \(location.y))")

            if behaviorMode == .ChangeWindowSize {
                frame = CGRect(origin: frame.origin, size: CGSize(width: location.x, height: location.y ))
            }else{
                frame = frame.offsetBy(dx: location.x - locationInitialTouch.x, dy: location.y - locationInitialTouch.y)
            }
        }
    }
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.locationInView(self)
            print("Ended:(\(location.x), \(location.y))")
            
            if behaviorMode == .ChangeWindowSize {
                frame = CGRect(origin: frame.origin, size: CGSize(width: location.x, height: location.y ))
            }else{
                frame = frame.offsetBy(dx: location.x - locationInitialTouch.x, dy: location.y - locationInitialTouch.y)
            }
            behaviorMode = .None
        }
    }
}
~~~

#### もしタップ検知しない場合

~~~swift
UIView.userInteractionEnabled = true
~~~