Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

iOS 개발일지

Modal 형태의 뷰를 스와이프를 통해 크기 조절해보자! (feat. UISwipeGestureRecognizer) 본문

iOS - 코드블럭 아카이브

Modal 형태의 뷰를 스와이프를 통해 크기 조절해보자! (feat. UISwipeGestureRecognizer)

Lia's iOS 2023. 5. 18. 09:31

 

    private lazy var upGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeGesture(_:)))
    private lazy var downGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeGesture(_:)))
    private var modalViewHeightConstraint: NSLayoutConstraint? // 챗뷰 높이 제약조건을 저장할 변수
    private var maxModalViewHeight: CGFloat = 0.0 // 최대 높이 저장할 변수
    private var minModalViewHeight: CGFloat = 0.0 // 최소 높이 저장할 변수

// 뷰의 크기가 정해지기 전에는 view.safeAreaLayoutGuide를 참조할 수 없으므로, viewDidLayoutSubviews에서 max, min 값 설정
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        maxModalViewHeight = view.safeAreaLayoutGuide.layoutFrame.height - dragView.frame.height - 16
        minModalViewHeight = view.safeAreaLayoutGuide.layoutFrame.height / 2 - dragView.frame.height
    }
    
    // 화면이 전부 로드되면 modalView를 위로 올리기
        override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        modalViewHeightConstraint?.constant = maxModalViewHeight
        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }

// 해당 예시에서는 모달뷰 상단에 드래그 영역을 만들어, 드래그뷰에 제스쳐 설정
    private func setDragView() {
        upGesture.direction = .up
        downGesture.direction = .down
        dragView.addGestureRecognizer(upGesture)
        dragView.addGestureRecognizer(downGesture)
        
        // 뷰 높이 제약 조건 초기화
        modalViewHeightConstraint = modalView.heightAnchor.constraint(equalToConstant: maxModalViewHeight)
        modalViewHeightConstraint?.isActive = true
        dragView.isUserInteractionEnabled = true
        dragView.backgroundColor = color.backgroundSub
        dragView.layer.cornerRadius = 22.5
        dragView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        dragBarView.backgroundColor = color.backgroundMajor
        dragBarView.cornerRadius = 2.5
    }

    @objc private func handleSwipeGesture(_ gesture: UISwipeGestureRecognizer) {
        guard !modalView.userTextView.isFirstResponder else { return }
        guard gesture.state == .ended else { return }
        switch gesture.direction {
        case .up:
            modalViewHeightConstraint?.constant = maxModalViewHeight // 최대 높이로 높이 변경
        case .down:
            modalViewHeightConstraint?.constant = minModalViewHeight // 최소 높이로 높이 변경
        default:
            break
        }
        
        // 변경된 높이 적용
        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }   
 
    private func setConstraints() {
        dragBarView.snp.makeConstraints { make in
            make.top.equalTo(dragView).offset(12)
            make.centerX.equalToSuperview()
            make.height.equalTo(5)
            make.width.equalTo(50)
        }
        
        chatView.snp.makeConstraints { make in
            make.top.equalTo(dragView.snp.bottom)
            make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide)
        }
    }

(현재 view의 크기를 참조해야 하기 때문에, 생명주기를 신경써야 하는 부분이 꽤 있다)

 

 

 

 

—번외: 키보드의 활성화를 감지하여 위의 modalView를 올리고 내리자!—

// 화면이 보일 때 옵저버 추가
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardUp), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDown), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

// 화면이 사라질 때 옵저버 삭제
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    @objc func keyboardUp(notification:NSNotification) { // 키보드가 올라오면 화면도 같이 올림
        if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue

// 만약 키보드가 활성화 된 상태일 때 modalView의 높이를 최대로 맞추고 싶다면 해당 코드 사용
            modalViewHeightConstraint?.constant = maxModalViewHeight - keyboardRectangle.height
            
            UIView.animate(withDuration: 0.3) {
                self.view.layoutIfNeeded()
                self.view.transform = CGAffineTransform(translationX: 0, y: -keyboardRectangle.height)
            }
        }
    }
    
    @objc func keyboardDown() { // 키보드가 내려가면 화면도 같이 내려감
        view.transform = .identity
        modalViewHeightConstraint?.constant = maxModalViewHeight // 최대 높이로 높이 변경
        
        // 변경된 높이 적용
        UIView.animate(withDuration: 0.3) {
            self.view.layoutIfNeeded()
        }
    }