-
[UIKit] - Tabbar 커스텀하기iOS/UIkit 2025. 3. 12. 15:39728x90반응형
오늘은 팀프로젝트를 하다가 생겼던 이슈들과 해결방법을 경험을 바탕으로 서술할 것이다.
먼저 Tabbar 부분이다. 기존 폼을 그대로 사용하되, 원하는 대로 커스텀을 하고 싶었다.
여기서 커스텀할 부분은 다음과 같다.
- 원본이미지를 Tabbar item으로 사용
- 해당 탭 선택 시 선택 안된 탭들은 투명처리
- 탭바의 높이
해결한 순과 쉬웠던 순?으로 정리한 것이다.
1. 원본이미지를 Tabbar item으로 사용
이 부분은 쉬웠다 단순하게 옵션을 선택해줬다.
해당 이미지를 홈 화면의 탭 아이템으로 쓰려고 했다.
그런데 막상 다음과 같이 구현을 하니homeView.tabBarItem = UITabBarItem(title: "홈", image: UIImage(named:"Home"),tag: 0)
?? 이렇게 나왔다. 뭔가 tintColor가 이미지를 덮고 있는 느낌?(mask가 생각난다.)
iOS에서 UIImage는 기본적으로 렌더링 모드(Rendering Mode)를 가지는데, 탭바 아이콘을 포함한 여러 UI 요소에서 이미지가 자동으로 색상이 변경될 수 있다.
🔍 렌더링 모드(Rendering Mode)란?
렌더링 모드는 iOS에서 UIImage가 UI에 표시될 때 어떻게 색상을 처리할지를 결정하는 속성으로 UITabBar, UINavigationBar, UIButton 등에서 아이콘 색상을 자동으로 변경할지, 원본 색상을 유지할지 결정할 수 있음.그래서 이 떄는
withRenderingMode(.alwaysOriginal)옵션을 사용하면 원본 색상을 유지할 수 있고, 다른 옵션으로는 .automatic(iOS에서 자동으로 설정으로 default값임) .alwaysTemplate(위 검정색 집처럼 항상 템플릿모드) 으로 되어있기 때문에 위와 같이 나왔던 것이다.
그래서 옵션을 추가해줬다.
homeView.tabBarItem = UITabBarItem(title: "홈", image: UIImage(named:"Home")?.withRenderingMode(.alwaysOriginal),tag: 0)
잘 나온다.
2. 해당 탭 선택 시 선택 안된 탭들은 투명처리
이부분도 상당히 애를 먹었다.
만약 시스템 이미지를 사용한다면, 주로 이 옵션을 사용했을 것이다.
self.tabBar.unselectedItemTintColor = .gray
이건 어디까지나 템플릿 모드일 때고, 원본이미지를 그대로 사용한다면 얘기가 달라진다.
그래서 이미지를 새로 그리는 방법을 선택했다.
extension UIImage{ func imageWithAlphaColor(alpha: CGFloat) -> UIImage? { return UIGraphicsImageRenderer(size: self.size).image { _ in self.draw(at: .zero, blendMode: .normal, alpha: alpha) } } }
• self.draw(at: .zero, blendMode: .normal, alpha: alpha)로 새로운 이미지 컨텍스트를 생성.
• .zero → (0,0) 위치에서 그리기 시작.
• blendMode: .normal → 색상 블렌딩 없이 원본 그대로 사용.
• alpha: alpha → 투명도를 적용해서 그림.
그러고 선택상황에 따라 원하는 투명도를 alpha 파라미터로 넘겨준다.
class TabBarView: UITabBarController,UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self tabItemUpdate() } private func tabItemUpdate(){ homeView.tabBarItem = UITabBarItem(title: "홈", image: UIImage(named:"Home")?.imageWithAlphaColor(alpha: tabItemTag == 0 ? 1.0:0.3)?.withRenderingMode(.alwaysOriginal),tag: 0) cardView.tabBarItem = UITabBarItem(title: "맴버카드", image: UIImage(named:"Member")?.imageWithAlphaColor(alpha: tabItemTag == 1 ? 1.0:0.3)?.withRenderingMode(.alwaysOriginal), tag: 1) } func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { tabItemTag = viewController.tabBarItem.tag tabItemUpdate() } }
tabBarController의 태그를 변수에 저장하고, 태그에 따라 투명도를 조절했다.
3. 탭바의 높이
이 부분이 제일 열받는 부분이, iOS 버전이 올라감(13.4부터)에 따라 원래 잘 작동하던 코드가 작동을 안한다는 것이다.
//기존 작동하던 코드 extension UITabBar { override open func sizeThatFits(size: CGSize) -> CGSize { super.sizeThatFits(size) var sizeThatFits = super.sizeThatFits(size) sizeThatFits.height = 100 return sizeThatFits } }
다음 링크를 참고했다.
UITabBar with custom height in Swift, does not work for iOS 14 or later.
UITabBar with custom height in Swift, does not work for iOS 14 or later. - Tabbar.Swift
gist.github.com
들어가서 보면 알겠지만, 오버라이딩 한 super.sizethatFits()가 호출되지 않아서 height업데이트가 이루어지지 않았던 것이다.
그래서 UItabbar프로토콜을 준수한 새로운 Tabbar를 정의하고 super.sizethatFits()를 호출해 문제를 해결했다.
class CustomTabBar : UITabBar { override open func sizeThatFits(_ size: CGSize) -> CGSize { var sizeThatFits = super.sizeThatFits(size) sizeThatFits.height = 100 return sizeThatFits } } class TabBarView: UITabBarController,UITabBarControllerDelegate { init() { super.init(nibName: nil, bundle: nil) object_setClass(self.tabBar, CustomTabBar.self) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
여기서 object_setClass를 처음봤는데, 되게 신기했다.
인스턴스의 타입을 런타임 시 바꾸는 메서드라고 한다.
그래서 UITaber타입인 self.tabBar를 CustomTabBar타입으로 변경한 것이다.
다만 기존 인스턴스의 타입과 새로 바구려는 타입의 모델이 같아야한다.
예를 들면 이런것이다. UITaber와 CustomTabBar 모두 sizethatFits메서드를 가지고 있고, 그것을 호출하기 때문에 가능한 것이지, 호출되는 하위 메서드가 다를 경우 런타임 에러가 난다.(런타임 시 캐스팅을 하는것이니..)
다음은 object_setClass에 대해 GPT가 경고한 내용이다.
🚨 주의할 점
1️⃣ Swift에서는 타입 시스템이 강해서 런타임 변경이 제한적
• object_setClass를 사용해도 기존에 선언된 타입과의 관계를 유지해야 함.
• Swift의 강한 타입 시스템 때문에 타입 캐스팅이 필요할 수도 있음.
2️⃣ 메모리 구조가 같아야 함
• 변경할 두 클래스는 메모리 레이아웃이 일치해야 함.
• 예를 들어, 인스턴스 변수가 다른 클래스로 변경하면 메모리 충돌이 발생할 수 있음.
3️⃣ NSObject 기반 클래스에서만 안전하게 사용 가능
• Swift의 순수 Struct, Enum, Class에서는 안전하지 않음.
• NSObject를 상속하는 클래스에서 주로 사용됨.🛠 object_setClass를 언제 사용하면 좋을까?
• UIKit에서 뷰의 클래스를 동적으로 변경해야 할 때
• 런타임에서 특정 객체의 동작을 변경해야 할 때
• 디버깅이나 테스트 목적으로 객체를 교체할 때많은 걸 얻은 오늘이였다..
'iOS > UIkit' 카테고리의 다른 글
[UIKit] - AutoLayout (0) 2024.01.17 [UIKit] - XIB와 NIB (0) 2024.01.17 [UIkit] - 코드베이스 VS 스토리보드 (0) 2024.01.10