안녕하세요. 개발하는 정주입니다.
오늘은 "Custom TabBar에 테두리(border) 추가하기"에 대해 알아보겠습니다.
지난 포스팅과 이어지는 내용입니다.
2022.06.23 - [🍎 iOS/iOS 개발] - [iOS] 모서리가 둥근 Rounded TabBar 만들기 - Custom TabBar
결과 미리 보기
오늘 알아볼 Custom TabBar의 결과를 먼저 보겠습니다.
지난 포스팅에서 만든 Rounded TabBar에 테두리를 넣은 모양입니다.
단순히 border만 넣으면 원하는대로 나오지 않는 분들이 많으실 겁니다. 저처럼요.. ㅎㅎ
그래서 이번 포스팅을 하기로 결정했습니다.
border만 설정한 모습
그냥 탭바에 border를 설정하면 되는거 아니야? 라고 생각할 수 있습니다.
정말 그런지 한 번 확인해 봅시다.
지난 포스팅의 CustomTabBar 코드에 border를 추가해봤습니다.
private func setupTabBar() {
tabBar.tintColor = UIColor.mainColor
tabBar.backgroundColor = UIColor(named:"TabBarColor")
tabBar.layer.cornerRadius = (tabBar.frame.height + 10) * 0.30
tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
tabBar.layer.borderWidth = 3
tabBar.layer.borderColor = UIColor.red.cgColor
}
상단 모서리가 둥글고 border를 가진 탭바가 생성될 것입니다.
하지만 실제 결과물을 보면 원하는 결과가 아닙니다.
저희가 원하는 것은 상단 부분만 테두리가 감싸는 모습인데, 실제 결과물은 모든 면을 테두리가 감싸고 있습니다.
위쪽에만 테두리를 넣는 방법은 두 가지가 있습니다.
Custom TabBar에 SubView를 넣는 방법
Custom TabBar 클래스에 SubView를 넣는 방법입니다.
탭바에 테두리를 넣는 것이 아니라 테두리 뷰 위에 탭바를 그리는 방법입니다.
탭바가 모든 화면에서 표시된다면 추천드리고 싶은 방법입니다.
1. Custom TabBar 클래스에 UIView를 추가합니다.
class CustomTabBarController: UITabBarController {
var borderView: UIView!
...
}
테두리로 사용할 borderView를 추가합니다.
2. borderView를 생성하고 속성을 설정합니다.
private func setupBorderView() {
borderView = UIView(frame: .zero)
borderView.translatesAutoresizingMaskIntoConstraints = false
borderView.backgroundColor = .red
borderView.layer.cornerRadius = tabBar.frame.height * 0.30
borderView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.addSubview(borderView)
borderView.widthAnchor.constraint(equalToConstant: tabBar.frame.width + 3).isActive = true
borderView.heightAnchor.constraint(equalToConstant: tabBar.frame.height).isActive = true
borderView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
borderView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -3).isActive = true
}
UIView를 생성하고 Auto Layout을 설정하기 위해 traslatesAutoresizeingMaskIntoConstraints를 false로 설정합니다.
View에 기본적으로 적용되어 있는 Constraints를 활성화/비활성화 하는 속성입니다.
backgroundColor는 원하는 테두리 색상을 설정하고 cornerRadius와 maskedCorners는 탭바와 동일하게 설정합니다.
설정 후 UITabBarController의 view에 borderView를 추가합니다.
UITabBarController는 UIViewController를 상속하므로 당연히 view도 추가할 수 있습니다.
view를 추가한 후 Auto Layout을 적용해야 합니다.
width와 bottomAnchor에 원하는 테두리 두께만큼 설정해 주세요.
이때, centerX를 설정하는 이유는 width + borderWidth를 해주기 때문에 중앙 정렬을 하지 않으면 한쪽으로 치우치기 때문입니다.
위 코드를 viewDidLoad( )에서 실행하면 위 사진처럼 나옵니다.
탭바 위에 borderView가 그려지기 때문이죠.
view.bringSubviewToFront(tabBar)
따라서 bringSubviewToFront( )를 이용해 tabBar를 맨 앞으로 배치해야 합니다.
bringSubviewToFront( )는 말그대로 View를 앞으로 가져오는 메서드입니다.
bringSubviewToFront( )를 쓰면 원하는대로 위쪽에만 테두리가 나옵니다.
문제점
이 방식은 한 가지 문제점이 있습니다.
tabBar를 숨길 때 borderView는 그대로 남아있게 되어 발생하는 문제입니다.
탭바가 있는 ViewController에서 화면을 이동할 때 탭바를 숨기고 싶습니다.
그럴 때는 pushViewController를 하기 전 아래 코드를 이용하면 되는데요.
vc.hidesBottomBarWhenPushed = true
화면 이동이 될 때 tabBar를 감춰주고 되돌아올 때 tabBar를 다시 보여주는 편리한 속성입니다.
그럼 화면 이동을 해볼까요?
화면 이동을 한 후의 모습을 보면 borderView는 숨겨지지 않습니다.
VC 상태를 이용해 borderView도 숨기고 다시 보여주는 등의 동작을 하더라도,
UIViewController의 생명 주기 메서드들이 호출되는 딜레이만큼 borderView가 어중간한 타이밍에 보이고 사라집니다.
궁금하시다면 push할 때 borderView를 숨기고 viewWillAppear( )에 borderView를 다시 보여주면 됩니다.
이 문제는 탭바를 숨겨야할 때 발생하므로 탭바를 항상 보이고 있는 앱이라면 문제가 되지 않습니다.
하지만 카멜레온 앱은 홈 화면, 더보기 화면이 아닌 곳에서는 탭바를 숨겨야 하기 때문에 문제가 있었습니다.
그래서 생각한 방법이 화면에 borderView를 추가하는 방식입니다.
화면 ViewController에 borderView를 넣는 방법
Custom TabBar가 아니라 각 화면에 borderView를 추가하면 되는 것이죠.
1. 필요한 view와 property 추가
private var tabbarBorderView: UIView!
private var tabbarHeight: CGFloat = 0
private var tabbarPadding: CGFloat = 0
borderView 용도인 tabbarBorderView, tabBar의 높이와 아래쪽 Padding 값 변수도 추가합니다.
tabbarPadding은 홈 버튼이 없는 기기에서 아래쪽 SafeArea padding 값입니다.
홈 버튼이 있는 기기는 padding 값이 0 입니다.
2. Height와 Padding 전달하기
저는 ViewController와 View를 분리하였기 때문에 View에 TabBar의 Height와 Padding 값을 전달해야 합니다.
convenience init( )을 이용하면 됩니다.
ViewController 코드
override func loadView() {
super.loadView()
homeView = HomeView(frame: self.view.frame,
tabbarHeight: self.tabBarController!.tabBar.frame.size.height,
tabbarPadding: self.tabBarController!.tabBar.safeAreaInsets.bottom)
self.view = homeView
}
ViewController의 loadView( )에서 View를 생성할 때 tabBarController의 tabBar에서 height와 SafeArea bottom 값을 전달합니다.
View 코드
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(frame: CGRect, tabbarHeight: CGFloat, tabbarPadding: CGFloat) {
self.init(frame: frame)
self.tabbarHeight = tabbarHeight
self.tabbarPadding = tabbarPadding
setup()
}
View의 convenience init( )에서 속성을 받아 설정합니다.
3. borderView 세팅
private func setupTabbarBorder() {
tabbarBorderView = UIView(frame: .zero)
tabbarBorderView.translatesAutoresizingMaskIntoConstraints = false
tabbarBorderView.backgroundColor = .red
tabbarBorderView.layer.cornerRadius = (tabbarHeight - tabbarPadding) * 0.30
tabbarBorderView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
self.addSubview(tabbarBorderView)
tabbarBorderView.widthAnchor.constraint(equalToConstant: self.frame.width + 3).isActive = true
tabbarBorderView.heightAnchor.constraint(equalToConstant: tabbarHeight).isActive = true
tabbarBorderView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
tabbarBorderView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -3).isActive = true
}
cornerRadius를 제외한 코드는 동일합니다.
cornerRaius에서 모서리 부분이 tabBar와 동일해야 합니다.
위에서 전달 받은 tabBar의 height는 SafeArea 부분까지 포함된 height입니다.
예를 들어, tabBar만의 높이가 30이고 SafeArea bottom이 10이라면 40이 전달 되는 것입니다.
즉, tabBar는 30 * 0.3 만큼 cornerRadius가 적용되고 borderView는 40 * 0.3만큼 cornerRadius가 적용되어 모양이 달라집니다.
따라서 padding 값을 따로 받아서 계산한 것이죠.
이렇게 tabBar와 모양이 동일하고 VC 이동을 해도 문제가 없는 tabBar의 borderView가 만들어집니다.
단점
이 방식의 단점은 탭바가 들어가 있는 모든 ViewController에 borderView를 넣어야 한다는 점입니다.
함수로 만든다면 코드의 길이가 그리 길어지지 않을테지만 추가로 작업이 들어간다는 것이 썩 유쾌하진 않네요.
마무리
오늘은 Custom TabBar에 rounded 테두리를 넣는 방법에 대해 알아보았습니다.
두 가지 방법을 소개해드렸는데요. 각 상황에 맞춰 방법을 선택하면 좋을 것 같습니다.
그리고 제가 모르는 더 좋은 방법이 있다면 댓글로 알려주시면 감사하겠습니다.
감사합니다!
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.