diff --git a/Handy/Handy-Storybook/Component/NavigationViewController.swift b/Handy/Handy-Storybook/Component/NavigationViewController.swift new file mode 100644 index 0000000..bc7577d --- /dev/null +++ b/Handy/Handy-Storybook/Component/NavigationViewController.swift @@ -0,0 +1,61 @@ +// +// 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.bgBasicStrong + viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled) + return viewController + }() + + let page2: UIViewController = { + let viewController = UIViewController() + viewController.view.backgroundColor = HandySemantic.bgStatusPositive + 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.bgBasicLight + viewController.tabBarItem = UITabBarItem(title: "Label", image: HandyIcon.icHomeFilled, selectedImage: HandyIcon.icHomeFilled) + return viewController + }() + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + self.setViewControllers([page1, page2, page3, page4, page5], animated: true) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + } +} diff --git a/Handy/Handy-Storybook/SceneDelegate.swift b/Handy/Handy-Storybook/SceneDelegate.swift index 9b73cc0..cbb779e 100644 --- a/Handy/Handy-Storybook/SceneDelegate.swift +++ b/Handy/Handy-Storybook/SceneDelegate.swift @@ -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() } diff --git a/Handy/Handy.xcodeproj/project.pbxproj b/Handy/Handy.xcodeproj/project.pbxproj index 7456aa6..b18b6af 100644 --- a/Handy/Handy.xcodeproj/project.pbxproj +++ b/Handy/Handy.xcodeproj/project.pbxproj @@ -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 */; }; @@ -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 */; }; @@ -112,6 +111,8 @@ 2D41E8152C5A21B50043161D /* HandyFab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyFab.swift; sourceTree = ""; }; A56B3DE12C4E51D300C3610A /* HandyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyChip.swift; sourceTree = ""; }; A5A12A7C2C57A6C200996916 /* ChipViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipViewController.swift; sourceTree = ""; }; + A5C208A22CDCCD75005F20A1 /* HandyNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyNavigation.swift; sourceTree = ""; }; + A5C208C52CE3BCE1005F20A1 /* NavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewController.swift; sourceTree = ""; }; A5F6D36A2C96F32D00FB961F /* HandyDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyDivider.swift; sourceTree = ""; }; A5F6D36C2C97099C00FB961F /* DividerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DividerViewController.swift; sourceTree = ""; }; E51FBF9A2C5399A00097B0DA /* CheckBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxViewController.swift; sourceTree = ""; }; @@ -120,9 +121,6 @@ E5650D422C4D326D002790CC /* HandyCheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyCheckBox.swift; sourceTree = ""; }; E5650D442C4E366F002790CC /* HandyIcon.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyIcon.xcassets; sourceTree = ""; }; E5650D462C512B07002790CC /* HandyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyIcon.swift; sourceTree = ""; }; - E5650D422C4D326D002790CC /* HandyCheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyCheckBox.swift; sourceTree = ""; }; - E5650D442C4E366F002790CC /* HandyIcon.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyIcon.xcassets; sourceTree = ""; }; - E5650D462C512B07002790CC /* HandyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyIcon.swift; sourceTree = ""; }; E5669A3E2C443E7300DABC21 /* HandyBasicColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyBasicColor.swift; sourceTree = ""; }; E5669A422C443FE500DABC21 /* HandyBasicColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyBasicColor.xcassets; sourceTree = ""; }; E5D02AFC2C46C5A70056CE7B /* HandySematic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandySematic.swift; sourceTree = ""; }; @@ -152,6 +150,7 @@ 025776332C4EA98C00272EC6 /* Handy-Storybook */ = { isa = PBXGroup; children = ( + A5C208C22CE3BC53005F20A1 /* Component */, 025776482C4EB0E700272EC6 /* Atom */, 025776572C4EB7C700272EC6 /* Font */, 0257765A2C4EB9B800272EC6 /* Storybook */, @@ -229,7 +228,6 @@ 2D41E8152C5A21B50043161D /* HandyFab.swift */, A56B3DE12C4E51D300C3610A /* HandyChip.swift */, A5F6D36A2C96F32D00FB961F /* HandyDivider.swift */, - E5650D422C4D326D002790CC /* HandyCheckBox.swift */, E51FBF9F2C54CB260097B0DA /* HandyRadioButton.swift */, E5650D422C4D326D002790CC /* HandyCheckBox.swift */, ); @@ -268,6 +266,7 @@ 02BDB7F92C3E962D0050FB67 /* Source */ = { isa = PBXGroup; children = ( + A5C208A12CDCCD4E005F20A1 /* Component */, 02ED76482C577998001569F1 /* Extension */, 029C446B2C468F8E00331F61 /* Font */, 029E47FE2C49FD2E00D2F3B7 /* Atom */, @@ -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 */, @@ -310,6 +307,22 @@ path = Extension; sourceTree = ""; }; + A5C208A12CDCCD4E005F20A1 /* Component */ = { + isa = PBXGroup; + children = ( + A5C208A22CDCCD75005F20A1 /* HandyNavigation.swift */, + ); + path = Component; + sourceTree = ""; + }; + A5C208C22CE3BC53005F20A1 /* Component */ = { + isa = PBXGroup; + children = ( + A5C208C52CE3BCE1005F20A1 /* NavigationViewController.swift */, + ); + path = Component; + sourceTree = ""; + }; E5650D412C4D30B9002790CC /* Asset */ = { isa = PBXGroup; children = ( @@ -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 */, @@ -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 */, @@ -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 */, diff --git a/Handy/Handy/Source/Component/HandyNavigation.swift b/Handy/Handy/Source/Component/HandyNavigation.swift new file mode 100644 index 0000000..8696df4 --- /dev/null +++ b/Handy/Handy/Source/Component/HandyNavigation.swift @@ -0,0 +1,114 @@ +// +// HandyNavigation.swift +// Handy +// +// Created by 서준영 on 11/7/24. +// + +import UIKit + +open class HandyNavigation: UITabBarController { + + /// 내부에서 사용되는 레이아웃 수치입니다. + let itemImageVerticalInset: CGFloat = 10 + + /// 상단 탭바에 사용되는 디바이더입니다. + let divider = HandyDivider() + + open override func viewDidLoad() { + super.viewDidLoad() + setupView() + } + + /// 뷰 세팅 + private func setupView() { + 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)] // 텍스트 폰트 사이즈 + + /// 선택 되었을 때 설정 + 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 { + // FIXME: 레이아웃 적용 안됨 + // title이 있는 경우: 이미지와 텍스트 위치 조정 + tabBarItem.imageInsets = UIEdgeInsets( + top: itemImageVerticalInset, + left: 0, + bottom: 0, + right: 0 + ) + } + } + } +} + +/** + TODO: 레이아웃 간격 + - UITabBar의 높이 설정 불가 + - UITabBar의 아이콘 크기 설정 불가 + */ + +/** + 파일 변경점 + 1. Component폴더 생성 + 2. HandyNavigation.swift 생성 + 3. HandyIcon.swift 파일에 icHomeFilled 추가 + 4. Asset/HandyIcon에 이미지 추가 + */ diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/Contents.json b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/Contents.json b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/Contents.json new file mode 100644 index 0000000..81bdfc8 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icHome.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icHome@2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icHome@3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome.png b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome.png new file mode 100644 index 0000000..88a210c Binary files /dev/null and b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome.png differ diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@2.png b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@2.png new file mode 100644 index 0000000..a7572e3 Binary files /dev/null and b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@2.png differ diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@3.png b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@3.png new file mode 100644 index 0000000..8992294 Binary files /dev/null and b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/navigation/icHomeFilled.imageset/icHome@3.png differ diff --git a/Handy/Handy/Source/Foundation/HandyIcon.swift b/Handy/Handy/Source/Foundation/HandyIcon.swift index 05892f9..71ead37 100644 --- a/Handy/Handy/Source/Foundation/HandyIcon.swift +++ b/Handy/Handy/Source/Foundation/HandyIcon.swift @@ -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 {