Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Handy/Handy-Storybook/Component/NavigationViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// NavigationViewController.swift
// Handy-Storybook
//
// Created by 서준영 on 11/13/24.
//

import UIKit
import Handy
import SnapKit

class NavigationViewController: HandyNavigation {

let page1: UIViewController = {
let viewController = UIViewController()
viewController.view.backgroundColor = HandySemantic.bgStatusPositive
viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled)
return viewController
}()

let page2: UIViewController = {
let viewController = UIViewController()
viewController.view.backgroundColor = HandySemantic.bgBasicBlack
viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled)
return viewController
}()

let page3: UIViewController = {
let viewController = UIViewController()
viewController.view.backgroundColor = HandySemantic.bgStatusNegative
viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled)
return viewController
}()

let page4: UIViewController = {
let viewController = UIViewController()
viewController.view.backgroundColor = HandySemantic.bgBasicDefault
viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled)
return viewController
}()

let page5: UIViewController = {
let viewController = UIViewController()
viewController.view.backgroundColor = HandySemantic.iconBasicSecondary
viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled)
return viewController
}()

override func viewDidLoad() {
super.viewDidLoad()
setViewControllers([page1, page2, page3, page4, page5], animated: false)
}
}
2 changes: 1 addition & 1 deletion Handy/Handy-Storybook/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: UIScreen.main.bounds)
window?.windowScene = windowScene
window?.rootViewController = CheckBoxViewController()
window?.rootViewController = NavigationViewController()
window?.makeKeyAndVisible()
}

Expand Down
37 changes: 26 additions & 11 deletions Handy/Handy.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
A5A12A7F2C57A92000996916 /* HandySematic.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D02AFC2C46C5A70056CE7B /* HandySematic.swift */; };
A5A12A812C57A93C00996916 /* HandyPrimitive.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D02AFF2C480A180056CE7B /* HandyPrimitive.swift */; };
A5A12A822C57A93F00996916 /* HandyBasicColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5669A3E2C443E7300DABC21 /* HandyBasicColor.swift */; };
A5C208A32CDCCD75005F20A1 /* HandyNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C208A22CDCCD75005F20A1 /* HandyNavigation.swift */; };
A5C208C62CE3BCE1005F20A1 /* NavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C208C52CE3BCE1005F20A1 /* NavigationViewController.swift */; };
A5F6D36B2C96F32D00FB961F /* HandyDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5F6D36A2C96F32D00FB961F /* HandyDivider.swift */; };
A5F6D36D2C97099C00FB961F /* DividerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5F6D36C2C97099C00FB961F /* DividerViewController.swift */; };
E51FBF9B2C5399A00097B0DA /* CheckBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51FBF9A2C5399A00097B0DA /* CheckBoxViewController.swift */; };
Expand All @@ -49,9 +51,6 @@
E5650D432C4D326D002790CC /* HandyCheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D422C4D326D002790CC /* HandyCheckBox.swift */; };
E5650D452C4E366F002790CC /* HandyIcon.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5650D442C4E366F002790CC /* HandyIcon.xcassets */; };
E5650D472C512B07002790CC /* HandyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D462C512B07002790CC /* HandyIcon.swift */; };
E5650D432C4D326D002790CC /* HandyCheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D422C4D326D002790CC /* HandyCheckBox.swift */; };
E5650D452C4E366F002790CC /* HandyIcon.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5650D442C4E366F002790CC /* HandyIcon.xcassets */; };
E5650D472C512B07002790CC /* HandyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D462C512B07002790CC /* HandyIcon.swift */; };
E5669A3F2C443E7300DABC21 /* HandyBasicColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5669A3E2C443E7300DABC21 /* HandyBasicColor.swift */; };
E5D02AFD2C46C5A70056CE7B /* HandySematic.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D02AFC2C46C5A70056CE7B /* HandySematic.swift */; };
E5D02AFE2C46C9980056CE7B /* HandyBasicColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5669A422C443FE500DABC21 /* HandyBasicColor.xcassets */; };
Expand Down Expand Up @@ -112,6 +111,8 @@
2D41E8152C5A21B50043161D /* HandyFab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyFab.swift; sourceTree = "<group>"; };
A56B3DE12C4E51D300C3610A /* HandyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyChip.swift; sourceTree = "<group>"; };
A5A12A7C2C57A6C200996916 /* ChipViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipViewController.swift; sourceTree = "<group>"; };
A5C208A22CDCCD75005F20A1 /* HandyNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyNavigation.swift; sourceTree = "<group>"; };
A5C208C52CE3BCE1005F20A1 /* NavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewController.swift; sourceTree = "<group>"; };
A5F6D36A2C96F32D00FB961F /* HandyDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyDivider.swift; sourceTree = "<group>"; };
A5F6D36C2C97099C00FB961F /* DividerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DividerViewController.swift; sourceTree = "<group>"; };
E51FBF9A2C5399A00097B0DA /* CheckBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxViewController.swift; sourceTree = "<group>"; };
Expand All @@ -120,9 +121,6 @@
E5650D422C4D326D002790CC /* HandyCheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyCheckBox.swift; sourceTree = "<group>"; };
E5650D442C4E366F002790CC /* HandyIcon.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyIcon.xcassets; sourceTree = "<group>"; };
E5650D462C512B07002790CC /* HandyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyIcon.swift; sourceTree = "<group>"; };
E5650D422C4D326D002790CC /* HandyCheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyCheckBox.swift; sourceTree = "<group>"; };
E5650D442C4E366F002790CC /* HandyIcon.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyIcon.xcassets; sourceTree = "<group>"; };
E5650D462C512B07002790CC /* HandyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyIcon.swift; sourceTree = "<group>"; };
E5669A3E2C443E7300DABC21 /* HandyBasicColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyBasicColor.swift; sourceTree = "<group>"; };
E5669A422C443FE500DABC21 /* HandyBasicColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyBasicColor.xcassets; sourceTree = "<group>"; };
E5D02AFC2C46C5A70056CE7B /* HandySematic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandySematic.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -152,6 +150,7 @@
025776332C4EA98C00272EC6 /* Handy-Storybook */ = {
isa = PBXGroup;
children = (
A5C208C22CE3BC53005F20A1 /* Component */,
025776482C4EB0E700272EC6 /* Atom */,
025776572C4EB7C700272EC6 /* Font */,
0257765A2C4EB9B800272EC6 /* Storybook */,
Expand Down Expand Up @@ -229,7 +228,6 @@
2D41E8152C5A21B50043161D /* HandyFab.swift */,
A56B3DE12C4E51D300C3610A /* HandyChip.swift */,
A5F6D36A2C96F32D00FB961F /* HandyDivider.swift */,
E5650D422C4D326D002790CC /* HandyCheckBox.swift */,
E51FBF9F2C54CB260097B0DA /* HandyRadioButton.swift */,
E5650D422C4D326D002790CC /* HandyCheckBox.swift */,
);
Expand Down Expand Up @@ -268,6 +266,7 @@
02BDB7F92C3E962D0050FB67 /* Source */ = {
isa = PBXGroup;
children = (
A5C208A12CDCCD4E005F20A1 /* Component */,
02ED76482C577998001569F1 /* Extension */,
029C446B2C468F8E00331F61 /* Font */,
029E47FE2C49FD2E00D2F3B7 /* Atom */,
Expand All @@ -283,8 +282,6 @@
E5669A3E2C443E7300DABC21 /* HandyBasicColor.swift */,
E5D02AFF2C480A180056CE7B /* HandyPrimitive.swift */,
E5D02AFC2C46C5A70056CE7B /* HandySematic.swift */,
E5D02AFF2C480A180056CE7B /* HandyPrimitiveColor.swift */,
E5D02AFC2C46C5A70056CE7B /* HandySematicColor.swift */,
029E47FF2C49FD4000D2F3B7 /* HandyTypography.swift */,
E5650D462C512B07002790CC /* HandyIcon.swift */,
E5650D412C4D30B9002790CC /* Asset */,
Expand All @@ -310,6 +307,22 @@
path = Extension;
sourceTree = "<group>";
};
A5C208A12CDCCD4E005F20A1 /* Component */ = {
isa = PBXGroup;
children = (
A5C208A22CDCCD75005F20A1 /* HandyNavigation.swift */,
);
path = Component;
sourceTree = "<group>";
};
A5C208C22CE3BC53005F20A1 /* Component */ = {
isa = PBXGroup;
children = (
A5C208C52CE3BCE1005F20A1 /* NavigationViewController.swift */,
);
path = Component;
sourceTree = "<group>";
};
E5650D412C4D30B9002790CC /* Asset */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -455,6 +468,7 @@
2D41E8142C5A21930043161D /* FabViewController.swift in Sources */,
A5A12A812C57A93C00996916 /* HandyPrimitive.swift in Sources */,
A5A12A822C57A93F00996916 /* HandyBasicColor.swift in Sources */,
A5C208C62CE3BCE1005F20A1 /* NavigationViewController.swift in Sources */,
A5A12A7E2C57A6D900996916 /* ChipViewController.swift in Sources */,
A5A12A7F2C57A92000996916 /* HandySematic.swift in Sources */,
A5F6D36D2C97099C00FB961F /* DividerViewController.swift in Sources */,
Expand All @@ -476,8 +490,8 @@
02ED76332C5284E6001569F1 /* HandyBoxButton.swift in Sources */,
E5D02AFD2C46C5A70056CE7B /* HandySematic.swift in Sources */,
E5D02B002C480A180056CE7B /* HandyPrimitive.swift in Sources */,
E5D02AFD2C46C5A70056CE7B /* HandySematicColor.swift in Sources */,
E5D02B002C480A180056CE7B /* HandyPrimitiveColor.swift in Sources */,
E5D02AFD2C46C5A70056CE7B /* HandySematic.swift in Sources */,
E5D02B002C480A180056CE7B /* HandyPrimitive.swift in Sources */,
E51FBFA02C54CB260097B0DA /* HandyRadioButton.swift in Sources */,
E5669A3F2C443E7300DABC21 /* HandyBasicColor.swift in Sources */,
02ED76312C5284BB001569F1 /* HandyButtonProtocol.swift in Sources */,
Expand All @@ -488,6 +502,7 @@
029E48002C49FD4000D2F3B7 /* HandyTypography.swift in Sources */,
E5650D432C4D326D002790CC /* HandyCheckBox.swift in Sources */,
029E47FD2C49FD1A00D2F3B7 /* HandyLabel.swift in Sources */,
A5C208A32CDCCD75005F20A1 /* HandyNavigation.swift in Sources */,
A56B3DE22C4E51D300C3610A /* HandyChip.swift in Sources */,
E5650D472C512B07002790CC /* HandyIcon.swift in Sources */,
E5650D472C512B07002790CC /* HandyIcon.swift in Sources */,
Expand Down
120 changes: 120 additions & 0 deletions Handy/Handy/Source/Component/HandyNavigation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// HandyNavigation.swift
// Handy
//
// Created by 서준영 on 11/7/24.
//

import UIKit

open class HandyNavigation: UITabBarController {

/// 내부에서 사용되는 레이아웃 수치입니다.
let itemImageVerticalInset: CGFloat = 10

/// 상단 탭바에 사용되는 디바이더입니다.
let divider = HandyDivider()

private let customTabBar = CustomTabBar()

open override func viewDidLoad() {
super.viewDidLoad()
setupView()
}

/// 뷰 세팅
private func setupView() {
setValue(customTabBar, forKey: "tabBar")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabBar의 appearance를 바꾸지 않고 UITabBarController의 tabBar 요소를 직접 갈아끼우는 방식을 사용하신 이유가 궁금합니다!

단순 사이즈만 조정하고 싶으신거라면, UITabBar의 사이즈를 조절하는 함수를 override 하는 방식으로도 가능할 것 같아서요! (아마 sizeThatFits일꺼에요)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이 UITabBarController의 tabBar를 customTabBar로 갈아끼워서 높이를 조절할 필요는 없다고 생각하여 UITabBarControllerdml tabBar의 높이를 조절하는 것으로 코드를 수정했습니다!

setProperties()
setLayouts()
}

/// 프로퍼티 세팅
private func setProperties() {
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground() // 불투명한 배경으로 설정
appearance.backgroundColor = HandySemantic.bgBasicDefault
appearance.shadowColor = .clear // 탭바 그림자 제거

// 탭바 아이템 설정
/// 기본 설정
let tabBarItemAppearance = UITabBarItemAppearance(style: .stacked)
tabBarItemAppearance.normal.iconColor = HandySemantic.iconBasicDisabled // 아이콘 색상
tabBarItemAppearance.normal.titleTextAttributes = [
.foregroundColor: HandySemantic.textBasicDisabled, // 텍스트 색상
.font: UIFont.systemFont(ofSize: 11)] // 텍스트 폰트 사이즈
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미리 선언된 타이포그래피를 재사용하는 것이 좋아보여요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HandyFont를 사용하여 코드 수정했습니다!


/// 선택 되었을 때 설정
tabBarItemAppearance.selected.iconColor = HandySemantic.iconBasicPrimary // 선택된 아이콘 색상
tabBarItemAppearance.selected.titleTextAttributes = [
.foregroundColor: HandySemantic.textBasicPrimary, // 선택된 텍스트 색상
.font: UIFont.systemFont(ofSize: 11)] // 텍스트 폰트 사이즈

// appearance의 각 레이아웃 스타일에 탭바 아이템 외형 설정을 적용
appearance.stackedLayoutAppearance = tabBarItemAppearance
appearance.compactInlineLayoutAppearance = tabBarItemAppearance
appearance.inlineLayoutAppearance = tabBarItemAppearance

tabBar.standardAppearance = appearance
tabBar.scrollEdgeAppearance = appearance
}

/// 레이아웃 세팅
private func setLayouts() {
tabBar.addSubview(divider)
divider.snp.makeConstraints {
$0.top.leading.trailing.equalToSuperview()
}
}
}

//MARK: - 탭바 아이템 중앙 정렬
extension HandyNavigation {
open override var viewControllers: [UIViewController]? {
didSet { setTabBarItemImageInsets() }
}

open override func setViewControllers(_ viewControllers: [UIViewController]?, animated: Bool) {
super.setViewControllers(viewControllers, animated: true)
setTabBarItemImageInsets()
}

/// title이 nil이라면 imageInsets을 조정해서 image를 중앙 정렬합니다.
public func setTabBarItemImageInsets() {
viewControllers?.forEach {
if $0.tabBarItem.title == nil {
// title이 없는 경우
$0.tabBarItem.imageInsets = UIEdgeInsets(top: itemImageVerticalInset, left: 0, bottom: -itemImageVerticalInset, right: 0)
} else {
tabBarItem.imageInsets = UIEdgeInsets(
top: itemImageVerticalInset,
left: 0,
bottom: 0,
right: 0
)
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

특정 title만 nil이고 나머지는 아닌 경우는 엄청 드물 것 같아요!
또한, title도 nil일 수 있지만 icon도 nil일 경우도 배제하면 안될 것 같구요!

저는 HandyNavigation에 이를 조절할 수 있는 프로퍼티가 있다면 좋을 것 같아요! tabBar의 title을 설정할 때도 navigation title로 설정할 수도 있기 때문에 정말 이미지를 가운데에 놓고 싶다면 이를 결정하는 프로퍼티가 따로 있는 편이 더 편할 것 같아요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

centerItem 프로퍼티를 생성하여 HandyNavigation 생성시 centerItem = true로 설정했을 때만 모든 tabBarItem의 title을 nil로 바꾸고 아이콘을 중앙에 배치하는 것으로 수정했습니다!

    override func viewDidLoad() {
        super.viewDidLoad()
        centerItem = false
        setViewControllers([page1, page2, page3, page4, page5], animated: false)
    }

centerItem은 기본적으로 false입니다.


//MARK: - 네비게이션 바 레이아웃 설정 (Safe Area 처리)
class CustomTabBar: UITabBar {
let fixedHeight: CGFloat = 56

override func layoutSubviews() {
super.layoutSubviews()

// Safe Area를 고려한 높이 설정
var newFrame = self.frame
let safeAreaInsets: CGFloat = {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first {
return window.safeAreaInsets.bottom
}
return 0
}()
newFrame.size.height = fixedHeight + safeAreaInsets
newFrame.origin.y = UIScreen.main.bounds.height - newFrame.size.height
self.frame = newFrame
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "icHome.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Handy/Handy/Source/Foundation/HandyIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum HandyIcon {
public static var checkBoxLine: UIImage { .load(name: "checkBoxLine") }
public static var radioButtonLine: UIImage { .load(name: "radioButtonLine") }
public static var radioButtonDisabled: UIImage { .load(name: "radioButtonDisabled") }

public static var icHomeFilled: UIImage { .load(name: "icHomeFilled")}
}

extension UIImage {
Expand Down