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

[GWL-51] Design System Page Control 생성 #54

Merged
merged 4 commits into from
Nov 16, 2023
Merged
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
123 changes: 123 additions & 0 deletions iOS/Projects/Shared/DesignSystem/Sources/GWPageConrol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// GWPageConrol.swift
// DesignSystem
//
// Created by MaraMincho on 11/16/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import Foundation

import UIKit

// MARK: - GWPageControl

public final class GWPageControl: UIView {
let countOfPage: Int
var currentPageIndex: Int = 0
let spacing: CGFloat = 8
var pages: [GWRoundShadowView] = []
var pageswidthConstraint: [NSLayoutConstraint] = []

// MARK: - 과연 UIVIew를 optional로 만드는게 맞을까?

/// 2 와 5 사이 숫자를 입력하세요 아닐경우 nil이 리턴됩니다.
public init(count: Int = 2) {
countOfPage = (UIPageControlDefaultProperty.range).contains(count) ?
count :
UIPageControlDefaultProperty.numOfMinPage

super.init(frame: .init(origin: .zero, size: CGSize(width: 60, height: 10)))

makePages()
makePageConstraints()
selectPage(at: 0)
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError()
}

private enum UIPageControlDefaultProperty {
static let numOfMinPage = 2
static let numOfMaxPage = 5

static let range = numOfMaxPage ... numOfMaxPage

static let selectedPageColor = DesignSystemColor.main03
static let deselectedPageColor = DesignSystemColor.gray02

static let selectedPageWidth: CGFloat = 40
static let deselectedPageWidth: CGFloat = 10
}
}

private extension GWPageControl {
func makePages() {
pages = (0 ..< countOfPage).enumerated().map { _, _ -> GWRoundShadowView in
return pageViewObject
}
}

func makePageConstraints() {
var targetLeadingAnchor = safeAreaLayoutGuide.leadingAnchor
var targetSpacing: CGFloat = 0

pages.forEach { page in
// 중요: 맨 처음 Page객체는 왼쪽으로 붙여야 하기에 필수 불가결적으로 다음 로직이 필요합니다.
if targetLeadingAnchor != safeAreaLayoutGuide.leadingAnchor {
targetSpacing = spacing
}

addSubview(page)
page.leadingAnchor.constraint(equalTo: targetLeadingAnchor, constant: targetSpacing).isActive = true
page.heightAnchor.constraint(equalToConstant: spacing).isActive = true

let widthAnchor = page.widthAnchor.constraint(equalToConstant: UIPageControlDefaultProperty.deselectedPageWidth)
widthAnchor.isActive = true
pageswidthConstraint.append(widthAnchor)

targetLeadingAnchor = page.trailingAnchor
}
}

var pageViewObject: GWRoundShadowView {
let view = GWRoundShadowView(
shadow: .init(
shadowColor: DesignSystemColor.gray02.cgColor,
shadowOffset: CGSize(width: 0, height: 1),
shadowOpacity: 0.2, shadowRadius: 4.0
),
cornerRadius: 4,
backgroundColor: DesignSystemColor.gray03.cgColor
)

view.translatesAutoresizingMaskIntoConstraints = false
return view
}
}

public extension GWPageControl {
func selectPage(at pageIndex: Int) {
guard 0 ..< pages.count ~= pageIndex else {
return
}
let page = pages[pageIndex]
page.update(color: UIPageControlDefaultProperty.selectedPageColor)

let pageWidthConstraint = pageswidthConstraint[pageIndex]
pageWidthConstraint.constant = UIPageControlDefaultProperty.selectedPageWidth
}

func deselectPage(at pageIndex: Int) {
guard 0 ..< pages.count ~= pageIndex else {
return
}
let page = pages[pageIndex]
page.update(color: UIPageControlDefaultProperty.deselectedPageColor)

let pageWidthConstraint = pageswidthConstraint[pageIndex]
pageWidthConstraint.constant = UIPageControlDefaultProperty.deselectedPageWidth
}
}
66 changes: 66 additions & 0 deletions iOS/Projects/Shared/DesignSystem/Sources/GWRoundShadowView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// GWRoundShadowView.swift
// DesignSystem
//
// Created by MaraMincho on 11/16/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import UIKit

// MARK: - GWRoundShadowView

public final class GWRoundShadowView: UIView {
let containerView = UIView()
let cornerRadius: CGFloat
let customShadow: CustomShadow
private var shadowLayer: CAShapeLayer!
private var fillColor: CGColor = UIColor.blue.cgColor

public func update(color: UIColor) {
fillColor = color.cgColor
}

override public func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()

shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
shadowLayer.fillColor = fillColor

shadowLayer.shadowColor = customShadow.shadowColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = customShadow.shadowOffset
shadowLayer.shadowOpacity = customShadow.shadowOpacity
shadowLayer.shadowRadius = customShadow.shadowRadius
Comment on lines +29 to +36
Copy link
Member

Choose a reason for hiding this comment

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

저였다면 View 하나 더 두고 했었을텐데.. Layer를 활용하실 생각을 하셨군요. 역시..👍

Copy link
Member Author

@MaraMincho MaraMincho Nov 16, 2023

Choose a reason for hiding this comment

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

S020 어깨너머로... 더보기

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

?


layer.insertSublayer(shadowLayer, at: 0)
}

if shadowLayer.fillColor != fillColor {
shadowLayer.fillColor = fillColor
}
}

init(shadow: CustomShadow, cornerRadius: CGFloat, backgroundColor: CGColor) {
customShadow = shadow
self.cornerRadius = cornerRadius
fillColor = backgroundColor
super.init(frame: .zero)
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

// MARK: - CustomShadow

struct CustomShadow {
let shadowColor: CGColor
let shadowOffset: CGSize
let shadowOpacity: Float
let shadowRadius: CGFloat
}
Copy link
Member

Choose a reason for hiding this comment

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

p2: public으로 설정 안해줘도 괜찮나요? 다른 곳에서 사용할 때 init이 안 불려질 것 같아서요!

Copy link
Member Author

Choose a reason for hiding this comment

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

수정했습니다. Shadow또한 파일 분리 했습니다!