먹고 기도하고 코딩하라

[UIKit] 탭한 위치가 어느 뷰의 영역 안에 있는지 확인하기 본문

앱/Swift

[UIKit] 탭한 위치가 어느 뷰의 영역 안에 있는지 확인하기

사과먹는사람 2023. 8. 10. 19:59
728x90
728x90

 

1. 버튼이 아닌 일반 UIView에서 Touch up Inside 등의 탭 이벤트를 감지하려면, UITapGestureRecognizer를 뷰에 붙여줘야 한다. 또한, recognizer가 탭 이벤트를 감지했을 때 수행할 셀렉터 함수도 필요하다.

셀렉터 함수부터 만들어주자. 여기서는 tapSomewhere이라는 이름으로 만든다.

class ViewController: UIViewController {
    @IBOutlet weak var dimmedBGView: UIView!
    var bgViewTapGesture: UITapGestureRecognizer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        bgViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(tapSomewhere(_:)))
    }
    
    deinit {
        bgViewTapGesture = nil
    }
    
    @objc func tapSomewhere(_ gesture: UITapGestureRecognizer) {
        
    }
}

2. 뷰에 addGestureRecognizer로 recognizer를 붙여준다.

bgViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(tapSomewhere(_:)))
guard let bgViewTapGesture else { return }
dimmedBGView.addGestureRecognizer(bgViewTapGesture)

3. 셀렉터 함수 내부를 작성한다. 뷰 안에 이미지뷰 expandableImageView가 있는데, 그 이미지뷰 외부 영역에 대해 탭 이벤트가 발생했을 때 뷰가 숨겨지는 작업을 하겠다.

@IBOutlet weak var expandableImageView: UIImageView!
    
@objc func tapSomewhere(_ gesture: UITapGestureRecognizer) {
    if !expandableImageView.frame.contains(gesture.location(in: gesture.view)) {
        dimmedBGView.isHidden = true
    }
}

이처럼 탭 제스처의 이벤트가 일어난 영역의 좌표가 어떤 뷰 안에 포함되는지 확인하려면 frame.contains(:CGPoint)를 사용하면 된다. gesture.location(in: UIView?)는 탭 이벤트가 일어난 뷰의 좌표를 CGPoint로 돌려주기 때문에, contains(:CGPoint) 메소드를 사용할 수 있다.

 

이를 응용해서 이미지를 누르면 이미지가 크게 보이고 배경이 딤처리된 뷰가 화면을 덮는 등의 작업을 할 수 있다. 이미지가 아닌, 딤처리된 배경을 탭했을 때는 화면이 확대된 뷰가 숨겨지는 행위를 한다.

코드 전문은 아래와 같다. 여기서 사용한 UIImage의 extension resize 메소드는 이 블로그의 코드를 사용했다.

UIButton의 setImage를 통해 이미지를 셋팅할 때, 이미지 크기가 버튼 크기보다 크면 버튼에 constraint를 설정했더라도 그것을 무시하고 크기가 설정되기 때문에 적절한 resize는 필수다.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageButton: UIButton!
    @IBOutlet weak var dimmedBGView: UIView!
    @IBOutlet weak var expandableImageView: UIImageView!
    let imageURLStr: String = "<https://images.unsplash.com/photo-1535957998253-26ae1ef29506>"
    var bgViewTapGesture: UITapGestureRecognizer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        imageURLStr.loadAsyncImage { [weak self] image in
            guard let self else { return }
            let resizedImage: UIImage? = image?.resize(targetSize: .init(width: UIScreen.main.bounds.width - 100, height: ((UIScreen.main.bounds.width - 100) / 3) * 4))
            imageButton.setImage(resizedImage, for: .normal)
            expandableImageView.image = image
        }
        
        bgViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(tapSomewhere(_:)))
        guard let bgViewTapGesture else { return }
        dimmedBGView.addGestureRecognizer(bgViewTapGesture)
    }
    
    deinit {
        bgViewTapGesture = nil
    }

    @IBAction func onImageButton(_ sender: UIButton) {
        dimmedBGView.isHidden = false
    }
    
    @objc func tapSomewhere(_ gesture: UITapGestureRecognizer) {
        if !expandableImageView.frame.contains(gesture.location(in: gesture.view)) {
            dimmedBGView.isHidden = true
        }
    }
}

extension String {
    func loadAsyncImage(_ completion: @escaping (UIImage?) -> ()) {
        guard let url: URL = URL(string: self) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data,
                  response != nil,
                  error == nil else { return }
            DispatchQueue.main.async {
                completion(UIImage(data: data))
            }
        }.resume()
    }
}

extension UIImage {
    func resize(targetSize: CGSize) -> UIImage? {
        let newRect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height).integral
        UIGraphicsBeginImageContextWithOptions(newRect.size, true, 0)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        context.interpolationQuality = .high
        draw(in: newRect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

 

 

References

 

 

728x90
반응형
Comments