-
Notifications
You must be signed in to change notification settings - Fork 0
[iOS] traveline 디자인 시스템 구축기
공용 컴포넌트에 대한 디자인 시스템을 활용하면서 공용 폰트와 컬러, 디자인 등을 일관성 있게 유지하여 사용자 경험의 일관성을 주력ㄹ고 노력했습니다.
우리 앱의 컬러와 컨셉에 맞는 재사용 가능한 컴포넌트를 사용하여 개발 시간을 단축하고 효율성을 높이고자 했습니다.
TLNavigationBar는 저희가 사용하는 뷰에서 화면 상단에 위치한 UI 요소입니다. 뒤로가기나 완료, 타이틀 등의 요소에 공통적인 디자인 효과를 적용해 재사용하고 싶어 저희만의 TLNavigationBar를 만들게 되었습니다. 네비게이션 바가 필요한 곳에서 기존 네비게이션바를 대신해서 사용하고 있습니다.
아래와 같이 네비게이션 바가 필요한 곳에 선언하여 사용하면 됩니다. 여기서 주의할 점은 기존 navigationController의 navigationBar는 히든처리를 해줘야합니다. 그렇지않으면 기존의 네비게이션 바가 화면 상에 나타날 수 있습니다.
생성 시점에 타이틀을 설정해줘도 되고 추후에 변경할 일이 있다면 setupText(to:)
함수를 이용해 변경해 줄 수 있습니다.
private lazy var tlNavigationBar: TLNavigationBar = .init(title: "naviTitle", vc: self)
// ...
override func viewWillApear(_ animated: Bool) {
super.viewWillApear(animated)
navigationController?.navigationBar.isHidden = true
tlNavigationBar.setupText(to: "바꿀 텍스트")
}
// ...
완료 버튼이 필요한 경우에는 선언시점에 .addCompleteButton()
을 추가해서 완료 버튼의 유무를 설정할 수 있습니다.
TLNavigationBar를 사용하는 뷰컨트롤러에서 완료 버튼에 대한 액션처리는 TLNavigationBarDelegate를 채택해 쉽게 할 수 있습니다.
// ...
private lazy var tlNavigationBar: TLNavigationBar = .init(title: "naviTitle", vc: self)
.addCompleteButton()
override func viewDidLoad() {
super.viewDidLoad()
tlNavigationBar.delegate = self
}
// ...
extension MyViewController: TLNavigationBarDelegate {
func rightButtonDidTapped() {
// complete Button Action
}
}
접근 가능한 함수로 타이틀의 텍스트를 쉽게 바꿀 수 있고, 버튼의 활성화 역시 쉽게 변경할 수 있습니다.
func someFunction() {
tlNavigationBar.isRightButtonEnable(true)
}
UIAlertController와 똑같이 활용하시면 됩니다. traveline앱의 컬러와 폰트, 배경색을 적용한 UIAlertController입니다.
UIAlertAction의 style에 따라 다른 컬러가 적용됩니다.
- .cancel: TLColor.gray(#9F9F9F)
- .default: TLColor.main(#B15FFF)
- .destructive: TLColor.error(#FF4747)
func showAlert() {
let alert = TLAlertController(
title: "여행 일정 삭제",
message: "여행 일정 삭제 시 되돌릴 수 없어요.\n정말 삭제하시겠어요?",
preferredStyle: .alert
)
alert.addActions([
UIAlertAction(
title: "취소",
style: .cancel,
handler: {_ in
// cancel...
}),
UIAlertAction(
title: "삭제",
style: .destructive,
handler: {_ in
// destruct...
})
])
present(alert, animated: true, completion: nil)
}
먼저 TLBottomSheetVC는 기본적으로 빈 화면이기에 아래 예제코드와 같이 UIViewController처럼 상속을 받아서 사용합니다. 아래의 ExamlpeBottomSheet는 바텀시트에 텍스트필드를 추가한 예제코드입니다.
//
// ExampleBottomSheet.swift
// traveline
//
// Created by KiWoong Hong on 2023/11/16.
// Copyright © 2023 traveline. All rights reserved.
//
import UIKit
final class ExampleBottomSheet: TLBottomSheetVC {
// MARK: - UI Components
let textFiled: UITextField = .init()
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupAttributes()
setupLayout()
}
// MARK: - Functions
override func completeAction() {
let text = textFiled.text
delegate?.bottomSheetDidDisappear(data: text)
}
}
// MARK: - Setup Functions
extension ExampleBottomSheet {
func setupAttributes() {
textFiled.translatesAutoresizingMaskIntoConstraints = false
textFiled.backgroundColor = .blue
}
func setupLayout() {
main.addSubview(textFiled)
NSLayoutConstraint.activate([
textFiled.leadingAnchor.constraint(equalTo: main.leadingAnchor),
textFiled.trailingAnchor.constraint(equalTo: main.trailingAnchor),
textFiled.centerXAnchor.constraint(equalTo: main.centerXAnchor),
textFiled.centerYAnchor.constraint(equalTo: main.centerYAnchor)
])
}
}
사용할 상위 뷰의 뷰컨트롤러에서 바텀시트가 필요한 곳에 아래와 같이 선언하고 사용합니다.
(1) 바텀 시트의 완료 버튼은 인스턴스 생성 시점에 hasCompleteButton으로 설정할 수 있습니다. 이때 default 값은 false입니다.
(2) 바텀 시트의 높이도 조절할 수 있습니다. 마찬가지로 인스턴스 생성 시점에 detentHeight를 통해 높이를 조절할 수 있습니다. 이때 default 값은 UISheetPresentationController.Detent의 .medium()이며 이는 기기화면의 절반(하프모달)입니다.
private let button: UIButton = {
let button = UIButton()
button.setTitle("bottomSheetButton", for: .normal)
button.backgroundColor = TLColor.black
return button
}()
@objc private func buttonTapped() {
let sheet = ExampleBottomSheet(
title: "BottomSheet Title",
hasCompleteButton: false, // (1)
detentHeight: 500 // (2)
)
sheet.delegate = self
present(sheet, animated: true, completion: nil)
}
먼저 상위 뷰컨트롤러에 데이터를 전달하려면 TLBottomSheetVC를 상속받은 바텀시트에서 보내줄 completeAction을 재정의 해줘야합니다. 위에 전해줄 데이터를 delegate의 bottomSheetDidDisappear(data:)를 통해 전달해줍니다.
override func completeAction() {
let text = textField.text
delegate?.bottomSheetDidDisappear(data: text)
}
그럼 해당 데이터를 전달받을 상위 뷰 컨트롤러에서는 TLBottomSheetDelegate를 채택하고 해당 함수를 통해 전달받은 data를 사용하면 됩니다.
extension ViewController: TLBottomSheetDelegate {
func bottomSheetDidDisappear(data: Any) {
label.text = data as? String
}
}
TLToastView는 기본적으로 success, failure 두가지 타입이 있습니다.