구현 설계
- UINavigationController에 Extension하여 UINavigationBar에 프로필용 이미지 뷰를 적용할 수 있는 코드를 작성한다.
- AppManager 전역 싱글톤 인스턴스를 통해 프로필 용 이미지 뷰를 관리한다.
1. 네비게이션 바 코드 작성
1-1. UINavigationController Extension하기
이미지의 크기를 설정하기
→ Const 구조체 설정
extension UINavigationController{
/// WARNING: Change these constants according to your project's design
/// 앱 매니저용 싱글톤 인스턴스에 이 UIImageView 정보를 담아 놓아야 한다..!
private struct Const {
static let ImageSizeForLargeState: CGFloat = 40
static let ImageRightMargin: CGFloat = 16
static let ImageBottomMarginForLargeState: CGFloat = 12
static let NavBarHeightLargeState: CGFloat = 96.5
}
}
특정 뷰 컨트롤러에서 프로필 버튼을 넣기
extension UINavigationController{
...
// 특정 뷰 컨트롤러에 Account 매니저를 추가하려함
@MainActor func insertAccount(){
guard self.navigationBar.prefersLargeTitles else {
AppManager.shared.accountLogoView?.removeFromSuperview()
fatalError("It's not enabled perfersLargeTitles")
}
AppManager.shared.accountLogoView?.removeFromSuperview()
// 유저 계정 이미지가 있으면 그것을 가져오도록 코드를 수정해야함
**// accountImage와 accountLogoView와 다른 프로퍼티다!**
let imageView = UIImageView(image: AppManager.shared.**accountImage** ?? UIImage(systemName: "person.circle") )
// 내부적으로 구현한 메서드
setConstraints(imageView: imageView)
// 계정 이미지를 탭하면 바로 계정 설정에 넘어갈 수 있도록 설정한다.
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.goToAccount)))
AppManager.shared.accountLogoView = imageView
}
}
특정 뷰 컨트롤러에서 프로필 버튼을 없애기
extension UINavigationController{
...
// 앱 매니저의 슈퍼 뷰에서 없애주고 View를 비워줌
@MainActor func deleteAccount(){
AppManager.shared.accountLogoView?.removeFromSuperview()
AppManager.shared.accountLogoView = nil
}
}
스크롤 하며 LargeTitle에서 MediumTitle로 변환 대응하기
extension UINavigationController{
...
// 나중에 다른 아이폰에도 적용 가능하게 만들어야함!!
// 스크롤에 따라 계정 버튼 숨겨주기 기능
@MainActor func scrollAccountView(nowY: CGFloat){
let targetHeight:CGFloat = -103
let normTarget:CGFloat = -143 + 103
var norm = (nowY - targetHeight) / normTarget
norm = max(0,min(1,norm))
// 내부적으로 구현한 계산 프로퍼티
self.accountViewOpacity = Float(norm)
self.accountHidden = nowY > targetHeight
}
}
1-2. 위에 메서드를 구현을 돕기 위한 내부 메서드
내부 Constraint 및 Appearnce 설정 메서드
fileprivate extension UINavigationController{
@MainActor private func setConstraints(imageView: UIImageView){
self.navigationBar.addSubview(imageView)
// setup constraints
imageView.layer.cornerRadius = Const.ImageSizeForLargeState / 2
imageView.layer.cornerCurve = .circular
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -Const.ImageRightMargin),
imageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -Const.ImageBottomMarginForLargeState),
imageView.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState),
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
])
}
}
내부 뷰 스크롤 대응 설정 계산 프로퍼티
fileprivate extension UINavigationController{
@MainActor private var accountHidden:Bool{
get{ AppManager.shared.accountLogoView?.isHidden ?? false }
set{ AppManager.shared.accountLogoView?.isHidden = newValue }
}
@MainActor private var accountViewOpacity: Float{
get{ AppManager.shared.accountLogoView?.layer.opacity ?? -1.0 }
set{ AppManager.shared.accountLogoView?.layer.opacity = newValue }
}
}
프로필 이미지 선택시 계정 설정 ViewController로 이동
fileprivate extension UINavigationController{
@objc func goToAccount(){
let nav = UINavigationController(rootViewController: AccountVC())
self.present(nav,animated: true)
}
}
2. AppManager (전역 싱글톤)에 이미지 뷰 적용
💡 프로필 정보를 담은 UIImageView를 앱을 사용하는 동안 계속 저장할 공간이면 어떤 기법을 적용해도 상관 없다…
→ NotificationCenter를 사용해도 문제 없다는 의미..!
final class AppManager: NSObject{
// 프로필 이미지 뷰를 저장하는 프로퍼티
var accountLogoView: UIImageView?
// 프로필 이미지를 UserDefaults에 저장하는 프로퍼티
// 프로필 이미지 뷰를 처음 만들 때, 이 이미지를 먼저 가져온다.
@DefaultsState(\.profileImage) var accountImage {
didSet{ accountLogoView?.image = accountImage }
}
// var userName: String?
static let shared = AppManager()
private override init(){
super.init()
}
}
3. 뷰 컨트롤러에 사용
class ViewController: UIViewController{
...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.insertAccount()
}
...
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.navigationController?.deleteAccount()
}
}
스크롤 하는 경우
// UITableViewDeleate, UICollectionViewDeleate도 가능함
extension ViewController: UIScrollViewDelegate{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.navigationController?.scrollAccountView(nowY: nowY)
}
}
✅ scrollAccountView 메서드는 내부 스크롤 영역을 감지하는 숫자를 기기에 대응할 필요가 있음..!
✅ 프로필 이미지 크기를 기기 별로 대응할 필요가 있음..!
'이모저모 > UIKit' 카테고리의 다른 글
다양한 셀을 그리는 DiffableDataSource 데이터 구성하기 #2 (0) | 2023.11.10 |
---|---|
다양한 셀을 그리는 DiffableDataSource 데이터 구성하기 #1 (0) | 2023.11.10 |
Modern UIKit Collection, TableView #2-3 (0) | 2023.09.03 |
Modern UIKit Collection, TableView #2-2 (0) | 2023.09.03 |
Modern UIKit Collection, TableView #2-1 (0) | 2023.09.03 |
댓글