Notification
- 이벤트 자체
- 이벤트나 데이터의 특별한 형태로, 이름을 가지는 이벤트를 나타내는 클래스
- 앱 내에서 어떤 사건이 발생했음을 나타내며, 이를 통해 관련된 정보 전달 가능
- 주로 Notification.Name으로 식별
NotificationCenter
- 이벤트를 관리하고 전달하는 중간 매개체
- 앱 내에서 발생하는 이벤트들을 관리하고 이벤트를 발송하거나 수신하는 기능 제공
- 이벤트가 발생하면 NotificationCenter는 해당 이벤트를 관심있는 모든 옵저버에게 전달
- 이를 통해 다양한 객체 간에 이벤트를 공유하고 통신 가능
- iOS 앱에서 다양한 컴포넌트나 모듈 간의 통신을 도와주는 중요한 도구
- 객체들 간의 강한 의존성 없이 효율적으로 데이터나 이벤트를 공유 가능
사용자 로그인 이벤트 예제
// Notification을 사용한 예제
let loggedInNotification = Notification(name: Notification.Name("UserLoggedIn"), object: nil, userInfo: ["userID": "exampleUserID"])
NotificationCenter.default.post(loggedInNotification)
- Notification 을 생성하고 NotificationCenter를 통해 이벤트 발송
// NotificationCenter를 사용한 예제
NotificationCenter.default.addObserver(self, selector: #selector(userLoggedIn(_:)), name: Notification.Name("UserLoggedIn"), object: nil)
- NotificationCenter에 옵저버를 등록하고, 이벤트가 발생할 때 호출될 메서드 지정
Observer
- NotificationCenter를 통해 등록되는 이벤트 리스너
- 특정 이벤트가 발생했을 때 이를 감지하고 적절한 동작을 수행하는 역할 수행
- 객체 간에 느슨한 결합을 유지하며, 이벤트 발생 시 필요한 객체만 이를 처리하도록 도움
1. view 설정
- 화면에 아이디를 입력 받는 UI 구현
- baseView : 전체 화면을 커버하는 뷰
- titleLb : "아이디 입력" 텍스트를 보여주는 레이블
- textfield : 아이디를 입력하는 텍스트 필드
- bottomButton : 아래에 위치한 "다음" 버튼
- bottomBaseView : 화면 아래쪽을 커버하는 뷰
import UIKit
class KeyboardUpViewController: UIViewController {
lazy var baseView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var titleLb : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "아이디 입력"
return label
}()
lazy var textfield : UITextField = {
var view = UITextField()
view.translatesAutoresizingMaskIntoConstraints = false
view.placeholder = "아이디를 입력해주세요. "
return view
}()
lazy var bottomButton : UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("다음", for: .normal)
button.backgroundColor = .blue
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .bold)
return button
}()
lazy var bottomBaseView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "아이디 설정"
// view.backgroundColor = .red
view.addSubview(baseView)
baseView.addSubview(titleLb)
baseView.addSubview(textfield)
baseView.addSubview(bottomButton)
view.addSubview(bottomBaseView)
applyConstraints()
}
fileprivate func applyConstraints(){
let baseViewConstraints = [
baseView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
baseView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
baseView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
baseView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
]
let titleLbConstraints = [
titleLb.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
titleLb.topAnchor.constraint(equalTo: baseView.topAnchor, constant: 30)
]
let textfieldConstraints = [
textfield.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
textfield.topAnchor.constraint(equalTo: titleLb.bottomAnchor, constant: 20)
]
let bottomButtonConstraints = [
bottomButton.leadingAnchor.constraint(equalTo: baseView.leadingAnchor),
bottomButton.trailingAnchor.constraint(equalTo: baseView.trailingAnchor),
bottomButton.bottomAnchor.constraint(equalTo: baseView.bottomAnchor),
bottomButton.heightAnchor.constraint(equalToConstant: 50)
]
let bottomBaseViewContraints = [
bottomBaseView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
bottomBaseView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
bottomBaseView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bottomBaseView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]
NSLayoutConstraint.activate(baseViewConstraints)
NSLayoutConstraint.activate(titleLbConstraints)
NSLayoutConstraint.activate(textfieldConstraints)
NSLayoutConstraint.activate(bottomButtonConstraints)
NSLayoutConstraint.activate(bottomBaseViewContraints)
}
}
→ 키보드가 올라왔을 때 'bottomButton' 이 사라짐
- 이 문제를 해결하기 위해 NotificationCenter를 이용하여 키보드가 올라왔을 때의 이벤트 설정
2. keyboard의 움직임을 감지하는 Notification을 Observer 패턴으로 구독
// notification 추가하는 메서드
func addKeyboardNotifications(){
// 키보드가 나타날 때 앱에게 알리는 메서드 추가
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
// 키보드가 사라질 때 앱에게 알리는 메서드 추가
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
// notification 제거하는 메서드
func removeKeyboardNotifications(){
// 키보드가 나타날 때 앱에게 알리는 메서드 제거
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
// 키보드가 사라질 때 앱에게 알리는 메서드 제거
NotificationCenter.default.removeObserver(self, name:
UIResponder.keyboardWillHideNotification, object: nil)
}
// 키보드가 나타날 때 코드
@objc func keyboardWillShow(_ noti : NSNotification){
}
// 키보드가 사라졌을 때 코드
@objc func keyboardWillHide(_ noti : NSNotification){
}
'NotificationCenter.default.addObserver( ... )'
- NotificationCenter를 사용하여 키보드의 올라옴 이벤트를 감지할 수 있는 옵저버를 등록
- 'keyboardWillShowNotification' 이벤트가 발생할 때 'keyboardWillShow(_:))' 메서드를 호출하도록 설정
'NotificationCenter.default.removeObserver( ... )'
- 등록된 옵저버 제거
- 옵저러를 등록했던 메서드와 동일한 이벤트와 객체에 대한 정보를 사용하여 제거
3. keyboard 움직임을 감지하는 코드 작성
- 키보드가 올라온다면 bottomButton의 bottomAnchor.Constraints가 키보드 height 만큼 감소해야 함
키보드 활성화 : bottomButton.bottomAnchor.constraint.constant = -keyboardHeight
키보드 비활성화 : bottomButton.bottomAnchor.constraint.constant = 0
1) bottomButton의 bottomAnchor를 유동적으로 설정하기 위해 NSLayoutConstraint 타입의 변수 생성
var bottomButtonConstraint : NSLayoutConstraint?
2) bottomAnchor를 따로 빼내어 bottomButtonConstraint를 초기화하고 isActive로 활성화
let bottomButtonConstraints = [
bottomButton.leadingAnchor.constraint(equalTo: baseView.leadingAnchor),
bottomButton.trailingAnchor.constraint(equalTo: baseView.trailingAnchor),
bottomButton.heightAnchor.constraint(equalToConstant: 50)
]
// bottomAnchor를 따로 빼어 설정
bottomButtonConstraint = bottomButton.bottomAnchor.constraint(equalTo: baseView.bottomAnchor)
bottomButtonConstraint?.isActive = true
3) 키보드 높이 구해서 bottomAnchor 설정
// 키보드가 나타날 때 코드
@objc func keyboardWillShow(_ noti : NSNotification){
// 키보드의 높이만큼 화면을 올려줌
if let keyboardFrame : NSValue = noti.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] as? NSValue{
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
// bottomBaseView의 높이를 올려줌
// 노치 디자인인 경우 safe area를 계산함
if #available(iOS 11.0, *){
let bottomInset = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets.bottom ?? 0
let adjustedKeyboardHeight = keyboardHeight - bottomInset
//bottomBaseView의 높이를 올림
bottomButtonConstraint?.constant = -adjustedKeyboardHeight
} else {
// 노치 디자인이 없는 경우에는 원래대로 계산
bottomButtonConstraint?.constant = -keyboardHeight
}
// 화면 업데이트
view.layoutIfNeeded()
}
}
// 키보드가 사라졌을 때 코드
@objc func keyboardWillHide(_ noti : NSNotification){
// 키보드의 높이만큼 화면을 내려줌
bottomButtonConstraint?.constant = 0
view.layoutIfNeeded()
}
- keyboardFrame : NSValue 타입으로 키보드 프레임 정보를 저장할 변수
- noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] : 알림의 userInfo에서 키보드 프레임 정보를 가져옴
- keyboardFrame.cgRectValue : keyboardRectangle에 키보드 CGRect 값 할당하여 높이 구함
- #available(iOS 11.0, *) : iOS 11.0 이상에서만 해당 블록이 실행되도록 하는 역할
- UIApplication.shared.window : 현재 앱의 윈도우 목록을 가져옴
- first(where : {$0.isKeyWindow}) : 키보드가 표시된 상태의 윈도우를 가져옴
- 해당 윈도우의 safeAreaInsets속성을 사용하여 SafeArea 여백 정보를 가져옴
- adjustedKeyboardHeight : 키보드 높이에서 아래쪽 여백을 빼서 실제로 뷰를 위로 올려야 하는 높이 계산 후 constant 값 지정
4. 키보드 내려감 설정
// 화면 터치하면 키보드 내려감
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
전체 코드)
//
// KeyboardUpViewController.swift
// PracticeSwift
//
// Created by 이수현 on 2023/08/10.
//
import UIKit
class KeyboardUpViewController: UIViewController {
// 키보드 올라올 때 bottomButton의 bottomAnchor 설정을 위해 사용
var bottomButtonConstraint : NSLayoutConstraint?
lazy var baseView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var titleLb : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "아이디 입력"
return label
}()
lazy var textfield : UITextField = {
var view = UITextField()
view.translatesAutoresizingMaskIntoConstraints = false
view.placeholder = "아이디를 입력해주세요. "
return view
}()
lazy var bottomButton : UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("다음", for: .normal)
button.backgroundColor = .blue
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .bold)
return button
}()
lazy var bottomBaseView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "아이디 설정"
// view.backgroundColor = .red
view.addSubview(baseView)
baseView.addSubview(titleLb)
baseView.addSubview(textfield)
baseView.addSubview(bottomButton)
view.addSubview(bottomBaseView)
applyConstraints()
}
override func viewWillAppear(_ animated: Bool) {
addKeyboardNotifications()
}
override func viewWillDisappear(_ animated: Bool) {
removeKeyboardNotifications()
}
// notification 추가하는 메서드
func addKeyboardNotifications(){
// 키보드가 나타날 때 앱에게 알리는 메서드 추가
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
// 키보드가 사라질 때 앱에게 알리는 메서드 추가
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
// notification 제거하는 메서드
func removeKeyboardNotifications(){
// 키보드가 나타날 때 앱에게 알리는 메서드 제거
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
// 키보드가 사라질 때 앱에게 알리는 메서드 제거
NotificationCenter.default.removeObserver(self, name:
UIResponder.keyboardWillHideNotification, object: nil)
}
// 키보드가 나타날 때 코드
@objc func keyboardWillShow(_ noti : NSNotification){
// 키보드의 높이만큼 화면을 올려줌
if let keyboardFrame : NSValue = noti.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] as? NSValue{
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
// bottomBaseView의 높이를 올려줌
// 노치 디자인인 경우 safe area를 계산함
if #available(iOS 11.0, *){
let bottomInset = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets.bottom ?? 0
let adjustedKeyboardHeight = keyboardHeight - bottomInset
//bottomBaseView의 높이를 올림
bottomButtonConstraint?.constant = -adjustedKeyboardHeight
} else {
// 노치 디자인이 없는 경우에는 원래대로 계산
bottomButtonConstraint?.constant = -keyboardHeight
}
view.layoutIfNeeded()
}
}
// 키보드가 사라졌을 때 코드
@objc func keyboardWillHide(_ noti : NSNotification){
// 키보드의 높이만큼 화면을 내려줌
bottomButtonConstraint?.constant = 0
view.layoutIfNeeded()
}
// 화면 터치하면 키보드 내려감
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
// constarints 설정
fileprivate func applyConstraints(){
let baseViewConstraints = [
baseView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
baseView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
baseView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
baseView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
]
let titleLbConstraints = [
titleLb.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
titleLb.topAnchor.constraint(equalTo: baseView.topAnchor, constant: 30)
]
let textfieldConstraints = [
textfield.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
textfield.topAnchor.constraint(equalTo: titleLb.bottomAnchor, constant: 20)
]
let bottomButtonConstraints = [
bottomButton.leadingAnchor.constraint(equalTo: baseView.leadingAnchor),
bottomButton.trailingAnchor.constraint(equalTo: baseView.trailingAnchor),
bottomButton.heightAnchor.constraint(equalToConstant: 50)
]
bottomButtonConstraint = bottomButton.bottomAnchor.constraint(equalTo: baseView.bottomAnchor)
bottomButtonConstraint?.isActive = true
let bottomBaseViewContraints = [
bottomBaseView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
bottomBaseView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
bottomBaseView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bottomBaseView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]
NSLayoutConstraint.activate(baseViewConstraints)
NSLayoutConstraint.activate(titleLbConstraints)
NSLayoutConstraint.activate(textfieldConstraints)
NSLayoutConstraint.activate(bottomButtonConstraints)
NSLayoutConstraint.activate(bottomBaseViewContraints)
}
}
참고)
'Swift' 카테고리의 다른 글
Swift - DispatchQueue (0) | 2023.08.22 |
---|---|
Swift - UserDefaults (0) | 2023.08.16 |
Swift - 프로토콜 (Protocol) (0) | 2023.08.06 |
Swift 사진 설정하기 (UIImagePickerController 사용) (0) | 2023.08.05 |
Swift - UIKit, SnapKit 비교 (0) | 2023.07.23 |