관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UIView 화면에 보여지는 이벤트 구하는 방법(#스크롤 뷰 등장, #viewAppear, #convert(_:to:), #intersects) 본문

iOS 응용 (swift)

[iOS - swift] UIView 화면에 보여지는 이벤트 구하는 방법(#스크롤 뷰 등장, #viewAppear, #convert(_:to:), #intersects)

jake-kim 2023. 10. 13. 01:34

70UIlabel이 노출될때 이벤트 캐치하여 "보이는중"로 표시

ViewAppear 이벤트 아이디어

  • UIViewController는 viewWillAppear와 같은 뷰컨트롤러가 보이기 직전에 델리게이트 메소드가 있지만, UIView는 존재 x
  • UIView에서 viewAppear와 같은 메소드가 있다면, 특정 뷰가 등장했을때 파악이 가능
  • 등장했을때 해당 뷰의 frame값을 구하여, 이 뷰와 현재 화면의 bounds를 intersects하여 구하면 쉽게 파악이 가능
  • * intersects: 인자로 들어온 rect값에 호출한 instance의 bounds가 겹치는지 파악할때 사용하는 메소드

 

https://developer.apple.com/documentation/coregraphics/1454747-cgrectintersectsrect

  • CGRect의 확장으로 정의된 함수
extension CGRect {
    public func intersects(_ rect2: CGRect) -> Bool
}

viewAppear구현

* 예제 코드 준비) UIScrollView + UIStackView 안에 UILabel이 여러개 있는 형태

  • 코드
import UIKit

class ViewController: UIViewController {
    private let label = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 24, weight: .regular)
        label.numberOfLines = 0
        label.text = "보이는중"
        label.isHidden = true
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    private let scrollView = {
        let view = UIScrollView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private let stackView = {
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(label)
        view.addSubview(scrollView)
        scrollView.addSubview(stackView)
        
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        ])
        
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
        ])
        
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width),
        ])
        
        (0...100)
            .forEach { int in
                let label = {
                    let l = UILabel()
                    l.text = String(int)
                    if int == 70 {
                        l.text = String(int) + " <"
                    }
                    l.textAlignment = .center
                    l.textColor = .black
                    l.font = .systemFont(ofSize: 24)
                    return l
                }()
                stackView.addArrangedSubview(label)
            }
    }
}
  • 스크롤 될 때마다 프레임을 체크하여, 현재 보이는지 판단할 visibleInScreen 프로퍼티 준비
    • screen의 bounds에 보여지는지 확인할 뷰의 bounds값을 convert(_:to:)로 window기준, 상대 frame으로 변환 - convert(_:to:) 함수 구체적인 개념은 이전 포스팅 글 참고
    • 이 frame을 현재 화면 UIScreen.main.bounds와 겹치는지 intersects(_:)함수를 사용하여 비교
extension UIView {
    var visibleInScreen: Bool {
        guard !isHidden, window != nil, 0 < alpha else { return false }
        let screenBounds = UIScreen.main.bounds
        let viewFrame = convert(bounds, to: nil)
        return viewFrame.intersects(screenBounds)
    }
}
  • 이 함수를 스크롤 될때마다 체크하여, 보여질때 표시할 UILabel의 hidden값을 변경
scrollView.delegate = self

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let bounds = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: overView.frame.minY + labels[71].frame.height)
        label.isHidden = !labels[71].visibleIn(bounds: bounds)
    }
}

(결과)

응용 - A frame에 B뷰가 속하는지 판단하는 방법

  • 위에서 UIScreen.main.bounds 기준으로 판단했으므로, 이제 A frame값을 사용하여 A frame에 B뷰가 보이는지 판단이 가능
extension UIView {
    func visibleIn(bounds: CGRect) -> Bool {
        guard !isHidden, 0 < alpha else { return false }
        let viewFrame = convert(self.bounds, to: nil)
        return viewFrame.intersects(bounds)
    }
}

70UIlabel이 노출될때 이벤트 캐치하여 "보이는중"로 표시

* 전체 코드: https://github.com/JK0369/ExEventViewAppear

* 참고

https://developer.apple.com/documentation/uikit/uiview/1622442-convert

https://developer.apple.com/documentation/coregraphics/1454747-cgrectintersectsrect

Comments