Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REFACTOR]라이온하트 디자인시스템 구축 및 적용(#133) #134

Merged
merged 39 commits into from
Sep 30, 2023

Conversation

kimscastle
Copy link
Contributor

@kimscastle kimscastle commented Sep 25, 2023

[#133] REFACTOR : 라이온하트 디자인시스템 구축 및 적용

🌱 라이온하트 UI DesignSystem구축

  • 공용 component들을 구현하고 앱전체에 적용했습니다

🌱 PR Point

디자인 시스템 구축 이유

  • then라이브러리를 사용하지 않기로 결정했지만 UI관련 중복코드가 너무 많아서 가독성 측면에서 피로도가 너무 심해졌습니다
디자인시스템 적용 전 디자인시스템 적용 후
스크린샷 2023-09-25 오전 10 47 12 스크린샷 2023-09-25 오전 10 47 29
  • 이전 코드를 보면 UI를 선언할때 속성까지 같이 선언해줘야하는 구성이라 UI컴포넌트를 선언해주고 return해주는 코드가 공통적으로 들어가며 lable의 경우 필수적인 font나 color를 선언하는 부분때문에 불필요하게 코드가 길어지는 문제점이 있었습니다

  • 실제 ChallengeView의 경우에 UI적인 요소가 많은 View가 아님에도 사진아래부분까지 포함하면 UI선언부만 60줄이 넘어가게됩니다

  • 공용컴포넌트를 통해 디자인시스템을 구축하고 적용하면 60줄이 넘는 UI선언부가 10줄이하로 줄어들게됩니다

제작한 Component

스크린샷 2023-09-25 오전 10 57 28
  • 해당PR에서 제작하고 적용한 component들입니다, 추가로 필요한 컴포넌트가 있다면 해당 폴더에 추가해주면 될거같습니다

1.LHLabel

final class LHLabel: UILabel {

    init(type: Font.PretendardType,
         color: Palette,
         backgroundColor: Palette? = nil,
         alignment: NSTextAlignment = .left,
         lines: Int = 1,
         basicText: String? = nil) {
        super.init(frame: .zero)
        self.font = .pretendard(type)
        self.textColor = .designSystem(color)
        self.textAlignment = alignment
        self.text = basicText
        self.backgroundColor = .designSystem(.background)
        self.numberOfLines = lines
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 기본적으로 6개의 인자를 받을 수 있습니다, 현재 라이온하트에서 label을 만들때 사용되는 인자는 총 6개입니다
  • font와 color는 공통적으로 사용하고 alignment의 경우엔 공식문서에서 default가 left라고 해서 저렇게 설정을 해줬습니다, numberOfLines도 자주사용하는 옵션인데 default가 1이라 같게 설정해줬습니다, 가끔가다가 network를 통한 text값 설정이아닌 기본 text string을 가지고있는 label이 자주보여서 이 경우에는 기본 text를 설정할수있게 initalize에 basicText를 parameter로 넣어줬습니다

2.LHUnderLine

final class LHUnderLine: UIView {
    
    init(lineColor: Palette) {
        super.init(frame: .zero)
        self.backgroundColor = .designSystem(lineColor)
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 꽤나 자주사용하는 밑줄 컴포넌트입니다
  • 매번 UIView로 만들어서 사용하는 코드들이라서 color만 설정하면 되도록 만들었습니다

3. LHImageView

final class LHImageView: UIImageView {
    
    init(in image: UIImage? = nil, contentMode: UIView.ContentMode) {
        super.init(frame: .zero)
        self.image = image
        self.contentMode = contentMode
    }
    
    func makeRound(_ ratio: CGFloat) -> Self {
        self.clipsToBounds = true
        self.layer.cornerRadius = ratio
        return self
    }
    
    func opacity(_ ratio: Float) -> Self {
        self.layer.opacity = ratio
        return self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • UIImage의 경우에 많이 사용하는 컴포넌트였는데 모든 경우를 고려하기 쉽지 않아보여서 고민을 좀 했던 요소입니다
  • 기본적으로 image를 기본으로 가질수도 있거나 아닐수도있어서 init에서의 image의 경우엔 옵셔널로 처리했습니다
  • 프로젝트를 진행하면서 생각보다 contentMode를 까먹어서 이미지가 생각대로 안나왔던걸로 기억해서 기본값이 있겠지만 잊어먹지말라고 contentMode는 필수 param으로 initalize에다가 넣어줬습니다
  • 가끔가다가 round랑 opacity를 설정해주는 imageView가 있는데 좀 특수한 경우인거같아서 체이닝방식으로 처리하기 위해서 Self를 리턴해주는 메서드로 구현했습니다

4. LHCollectionView

final class LHCollectionView: UICollectionView {
    
    init(color: Palette? = .background, scroll: Bool = true) {
        super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        self.backgroundColor = .designSystem(color!)
        self.showsVerticalScrollIndicator = false
        self.showsHorizontalScrollIndicator = false
        self.isScrollEnabled = scroll
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • tableview의 경우엔 각view마다 너무 특성이 달라서 컴포넌트로 뺴지 못했는데 collectionview의 경우 팀내 코딩컨벤션에서 collectionview의 flowlayout은 collectionview선언부에서 선언해준다는 규칙이있었어서 해당 코드의 중복을 최소화하기 위해서 컴포넌트로 뺐습니다
  • 대부분의 backgrounColor는 고정인데 가끔다른게 있어서 defaults를 background로 해줬습니다, 대부분 scroll이 가능한데 성뷰에서만 scroll을 하면안되서 이부분을 처리해주기 위해서 scroll이라는 param을 true라는 default를 가지게 구현해줬습니다

5. LHStackView

final class LHStackView: UIStackView {
    
    init(axis: NSLayoutConstraint.Axis, spacing: CGFloat, distribution: UIStackView.Distribution = .fill) {
        super.init(frame: .zero)
        self.axis = axis
        self.spacing = spacing
        self.distribution = distribution
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • stackView의 경우엔 arrangedSubviews는 따로 빼는데 axis는 모든 stackview에서 공통이고 distribution도 공통적으로 선언해서 사용하고 있어서 spacing만 필수param으로 받아서 쉽게 구현할수있게 만들었습니다

6. LHToggleImageButton

final class LHToggleImageButton: UIButton {
    init(normal normalImage: UIImage?, select selectImage: UIImage?) {
        super.init(frame: .zero)
        self.setImage(normalImage, for: .normal)
        self.setImage(selectImage, for: .selected)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 버튼을 누르기 전과 후에 이미지가 바뀌는 버튼의 경우에 생각보다 코드가 길어지는 느낌이 들어서 normal일때의 image와 select일때의 image만 받아서 버튼을 만들어주는 컴포넌트를 구현했습니다
  • 추후에 클로저를 받아서 action도 생성할때 넣어줘도 괜찮을거같습니다

7. LHImageButton

final class LHImageButton: UIButton {
    init(setImage: UIImage?) {
        super.init(frame: .zero)
        self.setImage(setImage, for: .normal)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 라이온하트의 경우엔 글씨가 있는 roundbutton보다 단순 이미지를 가지고 있는 button이 훨씬 많다는걸 느꼈습니다 그래서 이미지만 넣어 버튼을 만들수있게 LHImageButton을 만들었습니다

8. LHView

final class LHView: UIView {
    init(color: UIColor?) {
        super.init(frame: .zero)
        self.backgroundColor = color
    }
    
    func makeRound(_ ratio: CGFloat) -> Self {
        self.clipsToBounds = true
        self.layer.cornerRadius = ratio
        return self
    }
    
    func opacity(_ ratio: Float) -> Self {
        self.layer.opacity = ratio
        return self
    }
    
    func maskedCorners(corners: CACornerMask) -> Self {
        self.layer.maskedCorners = corners
        return self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 생각보다 그냥 imageView를 다양한 용도로 사용한다는걸 코드를 보면서 알게되었습니다
  • 대부분 corner가있고 불투명한 backgroundColor를 가진 View를 사용해서 view자체의 기본속성은 color만 받게 하고 corner나 opacity그리고 maskedcorner의 경우엔 체이닝방식으로 구현할수있게 구현했습니다

9. LHLottie

final class LHLottie: LottieAnimationView {
    init() {
        super.init(frame: .zero)
        self.contentMode = .scaleAspectFill
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 라이온하트는 lottie를 많이 사용하는데 그때마다 lottie관련 중복코드가 너무 많아서 컴포넌트로 뺐습니다
  • 모든 lottie가 빈 lottie를 만들어놓고 network통신결과를 통해서 image가 input되기때문에 param없이 단순 생성만 해주면됩니다
  • lottie의 비율을 맞추기 위해 contentMode를 scaleAspectFill로 고정했습니다

🌱 제안 사항

1.Design System 수정

@ffalswo2 @sjk4618 @cchanmi

  • 각자의 뷰에 디자인시스템을 적용하는 과정에서 추가적인 메서드나 컴포넌트가 필요하다고 판단되면 제가 만든 컴포넌트를 수정하거나 해당폴더에 추가해야할거같습니다
  • 그리고 디자인시스템은 하나의 작은 수정만 있더라도 issue와 pr을 올리는게 좋을거같습니다
  • 모두가 사용할 공용 컴포넌트기때문에 빠르게빠르게 수정하고 적용했으면 좋겠습니다
  • 혹여나 추가 및 수정이 필요하면 저에게 말해주시면 제가 빠르게 branch를 파서 수정사항 적용하고 머지하겠습니다 그래야 작업에 차질이 없으면서도 최대한 충돌을 적게 발생시킬거같습니다

2.ViewController 선언 순서

  • 이건 단순 제안인데 ViewController가 이제 네트워크 객체, UI요소, 데이터요소이렇게 세가지가 선언되어있습니다
  • 제가 제안드리는 방식은 ViewController를 정리할때 네트워크객체, UI요소, Data요소순서로 정리하면 좋을거같습니다
  • 그리고 initalize, viewdidload순으로 정리하면 코드가 통일성 있어보일거같습니다
스크린샷 2023-09-25 오전 11 30 10

📮 관련 이슈

추후, 새 코드가 문제 없으면 삭제
변경후 레이어가 문제없어서 기존 레이어 삭제합니다
이미지버튼 컴포넌트
@kimscastle kimscastle self-assigned this Sep 25, 2023
@kimscastle kimscastle added ⚙️Settings 세팅 관련 🦁의성 의성's ♻️Refactoring 리펙터링 🎨DesignSystem and removed ⚙️Settings 세팅 관련 labels Sep 25, 2023
Copy link
Member

@sjk4618 sjk4618 left a comment

Choose a reason for hiding this comment

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

오오오 감사합니다!! 머지되면 제 뷰컨에 있는 것들 수정하겠습니다!!
정말 수고많으셨어요!!!!

@kimscastle kimscastle merged commit 3e424d7 into main Sep 30, 2023
@cchanmi
Copy link
Member

cchanmi commented Sep 30, 2023

PR 꼼꼼히 읽었습니다
수고하셨어요 김의성짱

@kimscastle kimscastle added this to the 🦁1차 Refactor🦁 milestone Oct 10, 2023
@kimscastle kimscastle deleted the refactor/#133-DesignSystem branch October 10, 2023 05:41
@kimscastle kimscastle changed the title [REFACTOR]라이온하트 디자인시스템 구축 및 적용 [REFACTOR]라이온하트 디자인시스템 구축 및 적용(#133) Oct 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[REFACTOR] 디자인시스템구축
3 participants