Skip to content

Commit

Permalink
[GWL-13] 운동 선택 화면 UI 구현 (#59)
Browse files Browse the repository at this point in the history
* feat: 기능 구현

* chore: sceneDelegate수정

* chor: CardCell에 Rounded기능과 DropShadow 함수 이름 수정

* chor: SceneDelegate 수정

* chor: 피드백 반영

오타 및 함수명 변경

* chore: 다크모드 대응

* chor: 피드백 반영

* chor: 디렉토리 이름 변경
  • Loading branch information
MaraMincho authored Nov 18, 2023
1 parent 84e95aa commit e2b0285
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 0 deletions.
1 change: 1 addition & 0 deletions iOS/Projects/App/Sources/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import RecordFeature
import UIKit

final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// WorkoutCardCell.swift
// RecordFeature
//
// Created by MaraMincho on 11/16/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import DesignSystem
import UIKit

// MARK: - WorkoutCardCell

class WorkoutCardCell: UICollectionViewCell {
static let identifier = "WorkoutCardCell"

override init(frame: CGRect) {
super.init(frame: frame)
makeShadowAndRounded()
backgroundColor = DesignSystemColor.primaryBackGround
setupConstraints()
}

override var isSelected: Bool {
didSet {
if isSelected {
makeSelectUI()
} else {
makeDeslectUI()
}
}
}

private let workoutIconDescriptionLabel: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .title3)
label.textAlignment = .center
label.text = "달리기에용"
label.contentMode = .scaleAspectFit

label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

private let workoutIcon: UIImageView = {
let config = UIImage.SymbolConfiguration(font: .systemFont(ofSize: 120))
let icon = UIImage(systemName: "figure.run", withConfiguration: config)
let imageView = UIImageView(image: icon)
imageView.contentMode = .scaleAspectFit
imageView.tintColor = DesignSystemColor.primaryText

imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()

required init?(coder: NSCoder) {
super.init(coder: coder)
}
}

private extension WorkoutCardCell {
func setupConstraints() {
contentView.addSubview(workoutIconDescriptionLabel)
workoutIconDescriptionLabel.bottomAnchor
.constraint(equalTo: contentView.bottomAnchor, constant: -12).isActive = true
workoutIconDescriptionLabel.leadingAnchor
.constraint(equalTo: contentView.leadingAnchor, constant: 0).isActive = true
workoutIconDescriptionLabel.trailingAnchor
.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true

contentView.addSubview(workoutIcon)
workoutIcon.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
workoutIcon.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true
workoutIcon.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
workoutIcon.bottomAnchor.constraint(equalTo: workoutIconDescriptionLabel.topAnchor, constant: -15).isActive = true
}

func makeShadowAndRounded() {
let radius: CGFloat = 10
contentView.layer.cornerRadius = radius
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true

layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
layer.cornerRadius = radius
}

func makeSelectUI() {
workoutIcon.tintColor = DesignSystemColor.main03
workoutIcon.makeShadow()

workoutIconDescriptionLabel.textColor = DesignSystemColor.main03
workoutIconDescriptionLabel.font = .preferredFont(forTextStyle: .title3, with: .traitBold)
}

func makeDeslectUI() {
workoutIcon.tintColor = DesignSystemColor.primaryText
workoutIcon.disableShadow()

workoutIconDescriptionLabel.textColor = DesignSystemColor.primaryText
workoutIconDescriptionLabel.font = .preferredFont(forTextStyle: .title3)
}
}

private extension UIImageView {
func makeShadow() {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: -2, height: 2)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.3
layer.masksToBounds = false
}

func disableShadow() {
layer.shadowOpacity = 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// WorkoutEnvironmentSetupViewController.swift
// RecordFeature
//
// Created by MaraMincho on 11/15/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import DesignSystem
import UIKit

// MARK: - WorkoutEnvironmentSetupViewController

public final class WorkoutEnvironmentSetupViewController: UIViewController {
override public func viewDidLoad() {
super.viewDidLoad()
setup()
}

override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

insertTempSource()
}

lazy var contentNAV: UINavigationController = {
let nav = UINavigationController(rootViewController: workoutSelectView)

return nav
}()

private let workoutSelectView = WorkoutSelectViewController()

private let pageControl: GWPageControl = {
let pageControl = GWPageControl(count: Constant.countOfPage)

pageControl.translatesAutoresizingMaskIntoConstraints = false
return pageControl
}()

var dataSource: UICollectionViewDiffableDataSource<Int, UUID>!
var workoutCardCollectionView: UICollectionView!
}

private extension WorkoutEnvironmentSetupViewController {
func setup() {
view.backgroundColor = .systemBackground
setupViewHierarchyAndConstraints()
workoutCardCollectionView = workoutSelectView.workoutCardCollectionView

configureDataSource()
}

func configureDataSource() {
dataSource = .init(collectionView: workoutCardCollectionView, cellProvider: { collectionView, indexPath, _ in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WorkoutCardCell.identifier, for: indexPath)
return cell
})
}

func insertTempSource() {
var snapshot = dataSource.snapshot()
snapshot.deleteAllItems()
snapshot.appendSections([0])
snapshot.appendItems([.init(), .init(), .init(), .init(), .init()])

dataSource.apply(snapshot)
}

func setupViewHierarchyAndConstraints() {
let safeArea = view.safeAreaLayoutGuide

view.addSubview(pageControl)
pageControl.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 10).isActive = true
pageControl.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 23).isActive = true
pageControl.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -23).isActive = true
pageControl.heightAnchor.constraint(equalToConstant: 30).isActive = true

view.addSubview(contentNAV.view)
contentNAV.view.translatesAutoresizingMaskIntoConstraints = false
contentNAV.view.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
contentNAV.view.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
contentNAV.view.topAnchor.constraint(equalTo: pageControl.bottomAnchor).isActive = true
contentNAV.view.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
}

enum Constant {
static let countOfPage = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// WorkoutSelectViewController.swift
// RecordFeature
//
// Created by MaraMincho on 11/16/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import DesignSystem
import UIKit

// MARK: - WorkoutSelectViewController

final class WorkoutSelectViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
navigationController?.setNavigationBarHidden(true, animated: false)
}

private let workoutSelectDescriptionLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .title1, with: .traitBold)
label.textAlignment = .left
label.text = "1. 운동을 선택하세요"

label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

lazy var workoutCardCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: makeCollectionViewLayout())
collectionView.register(WorkoutCardCell.self, forCellWithReuseIdentifier: WorkoutCardCell.identifier)

collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()

private let nextButton: UIButton = {
let button = UIButton()
button.configurationUpdateHandler = UIButton.Configuration.main(label: "다음")

button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
}

private extension WorkoutSelectViewController {
func makeCollectionViewLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1))

let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(
top: Const.cellInsets,
leading: Const.cellInsets,
bottom: Const.cellInsets,
trailing: Const.cellInsets
)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalWidth(0.55))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

let section = NSCollectionLayoutSection(group: group)

return UICollectionViewCompositionalLayout(section: section)
}

func setupConstraints() {
let safeArea = view.safeAreaLayoutGuide

view.addSubview(workoutSelectDescriptionLabel)
workoutSelectDescriptionLabel.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
workoutSelectDescriptionLabel.leadingAnchor
.constraint(equalTo: safeArea.leadingAnchor, constant: ConstraintsGuideLine.value).isActive = true
workoutSelectDescriptionLabel.trailingAnchor
.constraint(equalTo: safeArea.trailingAnchor, constant: -ConstraintsGuideLine.value).isActive = true

view.addSubview(workoutCardCollectionView)
workoutCardCollectionView.topAnchor
.constraint(equalTo: workoutSelectDescriptionLabel.bottomAnchor, constant: 12).isActive = true
workoutCardCollectionView.leadingAnchor
.constraint(equalTo: safeArea.leadingAnchor, constant: ConstraintsGuideLine.value).isActive = true
workoutCardCollectionView.trailingAnchor
.constraint(equalTo: safeArea.trailingAnchor, constant: -ConstraintsGuideLine.value).isActive = true
workoutCardCollectionView.bottomAnchor
.constraint(equalTo: view.bottomAnchor, constant: -15).isActive = true

view.addSubview(nextButton)
nextButton.leadingAnchor
.constraint(equalTo: safeArea.leadingAnchor, constant: ConstraintsGuideLine.value).isActive = true
nextButton.trailingAnchor
.constraint(equalTo: safeArea.trailingAnchor, constant: -ConstraintsGuideLine.value).isActive = true
nextButton.bottomAnchor
.constraint(equalTo: safeArea.bottomAnchor, constant: -28).isActive = true
}

enum Const {
static let cellInsets: CGFloat = 5
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// ConstraintsGuideLine.swift
// DesignSystem
//
// Created by MaraMincho on 11/18/23.
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import Foundation

public enum ConstraintsGuideLine {
public static let value: CGFloat = 23
}

0 comments on commit e2b0285

Please sign in to comment.