Skip to content

[iOS] traveline 디자인 시스템 구축기

otoolz edited this page Dec 12, 2023 · 12 revisions

목차

1. 개요
2. TLLabel
3. TLNavigationBar
4. TLAlertController
5. TLBottomSheetVC
6. TLToastView

개요

공용 컴포넌트에 대한 디자인 시스템을 활용하면서 공용 폰트와 컬러, 디자인 등을 일관성 있게 유지하여 사용자 경험의 일관성을 주고자 노력했습니다.

우리 앱의 컬러와 컨셉에 맞는 재사용 가능한 컴포넌트를 사용하여 개발 시간을 단축하고 효율성을 높이고자 했습니다. TLNavigationBar, TLBottomSheet 등을 사용해 비슷한 요소에 대한 반복을 줄여, 실제로 반복되는 코드를 줄이고 사용성을 높였습니다.

TLLabel

traveline의 폰트와 사이즈가 적용된 TLLabel입니다.

가이드라인

기본 사용법

사용하고자하는 폰트와 컬러를 인스턴스 생성 시 설정해주면 TLLabel을 사용할 수 있습니다.

let titleLabel: TLLabel = .init(font: TLFont.body2, color: TLColor.main)

텍스트 설정이나 색상 변경같은 자주 사용하는 기능을 함수로 작성해둬 편의성을 높였습니다. 텍스트를 변경할 때는 setText(to:) 를 사용하여 변경합니다. 폰트 컬러를 변경할 때는 setColor(to:) 를 사용하여 변경합니다. 폰트를 변경할 때는 setFont(to:) 를 사용하여 변경합니다.

TLNavigationBar

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)
}

TLAlertController

가이드라인

기본 사용법

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

가이드라인

기본 사용법

먼저 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)
    }

BottomSheet의 상위 뷰컨트롤러에서 TLBottomSheetDelegate를 채택해서 데이터를 전달받기

먼저 상위 뷰컨트롤러에 데이터를 전달하려면 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

TLToastView는 유저들의 여러 액션에 대한 결과를 간단히 알려주고 싶을 때 사용하려고 공용 컴포넌트로 만들어 사용하게 됐습니다. 기본적인 동작은 1.5초간 딜레이되어 화면 하단에 노출되고, 1.0초동안 천천히 사라지는 동작을 수행합니다.

가이드라인

기본 사용법

TLToastView는 기본적으로 success, failure 두가지 타입이 있습니다. success 타입 사용시 보라계열의 토스트 알림이 뜨고, failure 타입 사용시 붉은색계열의 토스트 알림이 뜹니다. 기본 사용 방법은 토스트 메시지를 띄우기 원하는 곳에서 TLToastView를 만들고 TLToastView의 show함수 안에 토스트를 띄울 뷰를 입력하면 됩니다.

예를 들어, TimelineVC에서 수정하기 버튼을 눌렀을 때, 토스트를 띄우고 싶다면 아래와 같이 사용하시면 됩니다.

//        ...
    private func setNavigationRightButton(isOwner: Bool) {
        var menuItems: [UIAction] = []
        
        if isOwner {
            menuItems = [
                .init(title: Literal.Action.modify, handler: { _ in
                    // TODO: - 수정하기 연결
                    let toast = TLToastView(message: "타임라인 작성을 완료했어요!")
                    toast.show(in: self.view)
                }),
                .init(title: Literal.Action.delete, attributes: .destructive, handler: { _ in
                    // TODO: - 삭제하기 연결
                })
            ]
        }
//         ...
Clone this wiki locally