Notice
Recent Posts
Recent Comments
Link
관리 메뉴

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

[iOS - swift] 키보드와 동시에 뷰 올리는 방법 (키보드와 특정 뷰 사이 거리 유지) 본문

iOS 응용 (swift)

[iOS - swift] 키보드와 동시에 뷰 올리는 방법 (키보드와 특정 뷰 사이 거리 유지)

jake-kim 2025. 1. 9. 01:59

키보드 올리는 동시에 텍스트 필드도 올리기 (텍스트 필드와 키보드 최소 거리 50 유지)

키보드와 동시에 뷰 올리기

  • "키보드와 텍스트필드 거리가 최소 50은 유지해주세요"
  • 키보드가 올라가는 동시에, 키보드 상단과 텍스트 필드 하단의 거리가 최소 50이상 되도록하는 방법?

ex) 스크롤 처리를 해주지 않으면 키보드가 올라가고 아무런 처리가 없는 경우 텍스트필드가 영역이 가려짐

텍스트필드 영역이 가려짐

키보드와 동시에 뷰 올리는 방법

  • autolayout을 사용한다면 keyboard safe area를 사용하거나 scrollView.contentInset.bottom를 동적으로 수정해주어도 되지만 다른 방법이 존재
  • 키보드가 올라갈 때, 텍스트필드의 maxY좌표와 키보드의 높이를 계산해서 scrollView.setContentOffset(_:animated:)를 사용해도 자연스럽게 동작

구현

  • 뷰 준비
    • 뷰를 레이아웃 구성하는 부분은 핵심부분이 아니므로 생략 (전체 코드는 해당 포스팅 가장 하단에 존재)
class ViewController: UIViewController {
    private let scrollView = UIScrollView()
    private let contentView = UIView()
    private let textField = UITextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupViews()
        setupConstraints()
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func setupViews() {
		// ..
    }
    
    private func setupConstraints() {
		// .. 
    }
  • 키보드가 등장할 때 (willShow) 키보드의 height를 구해서 피팅해주는 코드
override func viewDidLoad() {
    super.viewDidLoad()
    ...
    setupKeyboardObservers()
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

private func setupKeyboardObservers() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(keyboardWillShow(_:)),
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

@objc private func keyboardWillShow(_ notification: Notification) {
    guard
        let userInfo = notification.userInfo,
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
    else { return }
    
    fitScrollingMinDistance(keyboardHeight: keyboardFrame.height)
}

private func fitScrollingMinDistance(keyboardHeight: CGFloat) {
    // TODO: 핵심코드 구현
}
  • fitScrollingMinDistance에서 구현
    • 텍스트필드 하단키보드 상단과의 거리를 구하기
      • 텍스트필드 하단은 textField.frame.maxY로 구하면 scrollView 원점 기준으로 그려지기 때문에 contentSize 영향을 받으므로 window기준인 상대좌표를 구해야함
      • 키보드 상단의 y좌표는 디바이스 전체 height에서 키보드의 height를 뺀 값
      • convert를 통해 상대좌표 구하는 개념은 이전 포스팅 글 참고
private func fitScrollingMinDistance(keyboardHeight: CGFloat) {
    let superView = view.window ?? scrollView
    let textFieldBottomY = textField.convert(textField.bounds, to: superView).maxY
    let visibleAreaHeight = superView.frame.height - keyboardHeight
}
  • 이제 이 값을 사용하여 스크롤을 얼마나 해야하는지 offsetY를 구하고, 이 offsetY값이 0보다 큰 경우가 스크롤이 필요한 경우이며 이 경우만 스크롤 처리
private func fitScrollingMinDistance(keyboardHeight: CGFloat) {
    let superView = view.window ?? scrollView
    let textFieldBottomY = textField.convert(textField.bounds, to: superView).maxY
    let visibleAreaHeight = superView.frame.height - keyboardHeight
    let minDistance = 50.0
    let offsetY = textFieldBottomY + minDistance - visibleAreaHeight
    
    guard offsetY > 0 else { return }
    
    let currentContentOffset = scrollView.contentOffset
    scrollView.setContentOffset(
        CGPoint(x: currentContentOffset.x, y: currentContentOffset.y + offsetY),
        animated: true
    )
    scrollView.layoutIfNeeded()
}

완성)

키보드 올리는 동시에 텍스트 필드도 올리기 (텍스트 필드와 키보드 최소 거리 50 유지)

전체 코드)

import UIKit

class ViewController: UIViewController {
    private let scrollView = UIScrollView()
    private let contentView = UIView()
    private let textField = UITextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupViews()
        setupConstraints()
        setupKeyboardObservers()
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    private func setupViews() {
        scrollView.backgroundColor = .lightGray
        view.addSubview(scrollView)
        
        contentView.backgroundColor = .white
        scrollView.addSubview(contentView)
        
        textField.placeholder = "Enter text"
        textField.borderStyle = .roundedRect
        textField.backgroundColor = .white
        contentView.addSubview(textField)
    }
    
    private func setupConstraints() {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        contentView.translatesAutoresizingMaskIntoConstraints = false
        textField.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        NSLayoutConstraint.activate([
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            contentView.heightAnchor.constraint(equalToConstant: 1000)
        ])
        
        NSLayoutConstraint.activate([
            textField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            textField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            textField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 500),
            textField.heightAnchor.constraint(equalToConstant: 40)
        ])
    }
    
    private func setupKeyboardObservers() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillShow(_:)),
            name: UIResponder.keyboardWillShowNotification,
            object: nil
        )
    }
    
    @objc private func keyboardWillShow(_ notification: Notification) {
        guard
            let userInfo = notification.userInfo,
            let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        else { return }
        
        fitScrollingMinDistance(keyboardHeight: keyboardFrame.height)
    }
    
    private func fitScrollingMinDistance(keyboardHeight: CGFloat) {
        let superView = view.window ?? scrollView
        let textFieldBottomY = textField.convert(textField.bounds, to: superView).maxY
        let visibleAreaHeight = superView.frame.height - keyboardHeight
        let minDistance = 50.0
        let offsetY = textFieldBottomY + minDistance - visibleAreaHeight
        
        guard offsetY > 0 else { return }
        
        let currentContentOffset = scrollView.contentOffset
        scrollView.setContentOffset(
            CGPoint(x: currentContentOffset.x, y: currentContentOffset.y + offsetY),
            animated: true
        )
        scrollView.layoutIfNeeded()
    }
}
Comments