scrollViewの上に置いてあるUIViewをロングプレスで操作するときに詰まった

環境

  • xcode
  • xcode9
  • swift
  • swift3
  • scrollView
  • longpress
  • ロングプレス

scrollViewの上に置いてあるUIViewをロングプレスで操作するとき詰まりました。二日間も詰まりました。痛恨の極みです。最近、本当にこういうのはよくないと思いました。遅い。遅すぎる。遅いよりは早い方がいい。15分わからなかったら相談する。これを自分の仕事に対する行動指針にする。

やりたいこと

  • スーパービューのscrollViewを追加する。
  • scrollViewにUIViewを追加する
  • UIViewをロングプレスする
  • 現在ロングプレスしてある位置にUIViewの中心を持ってくる
  • ロングプレスを動かす度にそれに合わせてUIViewの中心も移動する

うまくいった実行

うまくいった実行動画
on.gif

うまくいった実装

//
//  ViewController.swift
//  LongPressTutolial
//
//  Created by ryosuke-hujisawa on 2017/10/31.
//  Copyright © 2017年 ryosuke-hujisawa. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {

    var tapLocation: CGPoint = CGPoint()
    let SubView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 250, height: 250))
    let scrollView = UIScrollView()

    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.backgroundColor = UIColor.gray
        scrollView.frame = CGRect(x: 0, y: self.view.frame.height/2, width: self.view.frame.width, height: self.view.frame.height/2)
        scrollView.contentSize = CGSize(width: 1000, height: 600)
        scrollView.bounces = false
        scrollView.delegate = self
        scrollView.clipsToBounds = false
        self.view.addSubview(scrollView)

        let bgColor = UIColor.blue
        SubView.backgroundColor = bgColor
        self.scrollView.addSubview(SubView)
        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longPressed(_:)))
        self.SubView.addGestureRecognizer(longPressRecognizer)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @objc func longPressed(_ sender: UILongPressGestureRecognizer)
    {
        print("longpressed")
        tapLocation = sender.location(in: self.scrollView)
        SubView.center = tapLocation
    }
}

うまくいかなかった実行

on.gif

うまくいかなかった実装

//
//  ViewController.swift
//  ImageRotatedSwift3
//
//  Created by ryosuke-hujisawa on 2017/10/30.
//  Copyright © 2017年 ryosuke-hujisawa. All rights reserved.
//

import UIKit

var tapLocation: CGPoint = CGPoint()

class ViewController: UIViewController, UIScrollViewDelegate {

    let SubView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 250, height: 250))
    let scrollView = UIScrollView()
    var tapLocation: CGPoint = CGPoint()

    override func viewDidLoad() {
        super.viewDidLoad()

        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressView(sender:)))
        longPressGesture.minimumPressDuration = 1
        longPressGesture.allowableMovement = 50
        self.SubView.addGestureRecognizer(longPressGesture)
        scrollView.backgroundColor = UIColor.gray
        scrollView.frame = CGRect(x: 0, y: self.view.frame.height/2, width: self.view.frame.width, height: self.view.frame.height/2)
        scrollView.contentSize = CGSize(width: 1000, height: 600)
        scrollView.bounces = false
        scrollView.delegate = self
        let bgColor = UIColor.blue
        SubView.backgroundColor = bgColor
        SubView.isUserInteractionEnabled = true
        scrollView.addSubview(SubView)
        self.view.addSubview(scrollView)
    }

    @objc func longPressView(sender: UILongPressGestureRecognizer) {
        print("Long Press")
        tapLocation = sender.location(in: self.view)
        SubView.center = tapLocation

        }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

なぜ、こんなことになったのか

tapLocation = sender.location(in: self.view)の部分。現在タップしている位置の基準がスーパービューになっている。SubViewの中心を現在ロングプレスしているロケーションの位置に持ってこようとした場合、ロケーションがスーパービューだとおかしくなる。なぜなら、SubViewはスーパービューではなくscrollViewに追加されているからだ。例えば、scrollViewがViewの上(x100y100)の位置に追加されているとして、scrollの一番左を触ったとしたら、現在ロングプレスしているロケーションの位置は(x0,y0)になる。scrollViewの(x0,y0)の位置に追加されているSubViewの一番はじを触った場合、本当であれば、SubViewは(x-50,y-50)の位置にくるはずだが、実際現在選択されているロケーションの位置は(x100y100)なので、SubViewの中心はscrollViewの中の(x100y100)の位置に移動することになった。正しい実装では、self.SubView.addGestureRecognizer(longPressRecognizer)のようにSubViewにロングプレスを追加して、そして、sender.location(in: self.scrollView)のように、現在ロングプレスしている位置をscrollViewを基準にしている。SubViewはscrollViewに追加されているのだから、うまくいく。

藤沢瞭介(Ryosuke Hujisawa)
  • りょすけと申します。18歳からプログラミングをはじめ、今はフロントエンドでReactを書いたり、AIの勉強を頑張っています。off.tokyoでは、ハイテクやガジェット、それからプログラミングに関する情報まで、エンジニアに役立つ情報を日々発信しています!

未整理記事