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

Feat [#43] 마이페이지 뷰 SegmentControl 상단 고정 기능 구현 완료 #49

Merged
merged 10 commits into from
Jan 13, 2024
12 changes: 8 additions & 4 deletions DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
3C4993652B4F2471002A99CF /* MyPageSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */; };
3C4993672B4F2644002A99CF /* MyPageContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4993662B4F2644002A99CF /* MyPageContentViewController.swift */; };
3C4993692B4F2653002A99CF /* MyPageCommentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4993682B4F2653002A99CF /* MyPageCommentViewController.swift */; };
3C49936B2B4FF118002A99CF /* MyPageSegmentedControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */; };
3C6192ED2B3A719A00220CEB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C6192EC2B3A719A00220CEB /* AppDelegate.swift */; };
3C6192EF2B3A719A00220CEB /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C6192EE2B3A719A00220CEB /* SceneDelegate.swift */; };
3C6192F62B3A719C00220CEB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3C6192F52B3A719C00220CEB /* Assets.xcassets */; };
Expand Down Expand Up @@ -141,7 +140,6 @@
3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageSegmentedControl.swift; sourceTree = "<group>"; };
3C4993662B4F2644002A99CF /* MyPageContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageContentViewController.swift; sourceTree = "<group>"; };
3C4993682B4F2653002A99CF /* MyPageCommentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCommentViewController.swift; sourceTree = "<group>"; };
3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageSegmentedControlView.swift; sourceTree = "<group>"; };
3C6192E92B3A719A00220CEB /* DontBe-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DontBe-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
3C6192EC2B3A719A00220CEB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3C6192EE2B3A719A00220CEB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -500,6 +498,13 @@
path = Cells;
sourceTree = "<group>";
};
3C8A262D2B5194C100E549F3 /* Cells */ = {
isa = PBXGroup;
children = (
);
path = Cells;
sourceTree = "<group>";
};
3CE9C1252B4BE5970086E4A3 /* Write */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -556,6 +561,7 @@
children = (
3CF184C82B4EEBEA00816D5F /* Views */,
3CF184C92B4EEBF500816D5F /* ViewControllers */,
3C8A262D2B5194C100E549F3 /* Cells */,
3CEE4CBE2B500BA900F506AF /* Helpers */,
);
path = MyPage;
Expand All @@ -566,7 +572,6 @@
children = (
3CF184CC2B4EEC1900816D5F /* MyPageView.swift */,
3CF184D02B4EFF9900816D5F /* MyPageProfileView.swift */,
3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */,
3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */,
);
path = Views;
Expand Down Expand Up @@ -682,7 +687,6 @@
2A8D70C52B4D8079009F4C6C /* UIViewController+.swift in Sources */,
2A6D54C12B479B4300F9891E /* adjusted+.swift in Sources */,
2A8D70D12B4DD356009F4C6C /* JoinAgreementViewController.swift in Sources */,
3C49936B2B4FF118002A99CF /* MyPageSegmentedControlView.swift in Sources */,
3C2F54522B51224500E7BF01 /* MyPageAccountInfoViewController.swift in Sources */,
3CE9C1352B4C4BC20086E4A3 /* CircleProgressbar.swift in Sources */,
3C6193192B3A7AC700220CEB /* Config.swift in Sources */,

Choose a reason for hiding this comment

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

제시된 코드 패치의 주요 변경 사항은 다음과 같습니다:

  1. [...] MyPageSegmentedControlView.swift in Sources와 관련된 부분이 변경되었습니다.
    • 이 파일에 대한 참조(3C49936A2B4FF118002A99CF)가 제거되었습니다.
    • PBXBuildFilePBXFileReference에서 해당 파일을 참조하고 있던 항목들이 삭제되었습니다.

추가적으로, Cells 그룹에 대한 새로운 PBXGroup이 추가되었습니다. 하지만 현재로서는 비어 있습니다.

개선 제안 및 버그 위험 사항 등에 대해서는 보여진 코드만으로는 파악하기 어렵습니다. 전체 소스 코드나 실행 환경 정보를 알 수 없기 때문입니다. 이러한 세부 정보를 함께 제공해주시면 추가적인 도움을 드릴 수 있습니다.

Expand Down
17 changes: 13 additions & 4 deletions DontBe-iOS/DontBe-iOS/Presentation/Helpers/CircleProgressbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class CircleProgressbar: UIView {

var lineWidth: CGFloat = 2
var circleBackgroundColor: UIColor = .gray
var circleTintColor: UIColor = .black
var circleTintColor: UIColor = .clear

var value: Double? {
didSet {
Expand All @@ -33,7 +33,11 @@ final class CircleProgressbar: UIView {
override func draw(_ rect: CGRect) {
let bezierPath = UIBezierPath()

bezierPath.addArc(withCenter: CGPoint(x: rect.midX, y: rect.midY), radius: rect.midX - ((lineWidth - 1) / 2), startAngle: 0, endAngle: .pi * 2, clockwise: true)
bezierPath.addArc(withCenter: CGPoint(x: rect.midX, y: rect.midY),
radius: rect.midX - ((lineWidth) / 2),
startAngle: 0,
endAngle: .pi * 2,
clockwise: true)

bezierPath.lineWidth = lineWidth
circleBackgroundColor.set()
Expand All @@ -45,17 +49,22 @@ final class CircleProgressbar: UIView {
return
}


self.subviews.forEach { $0.removeFromSuperview() }
self.layer.sublayers?.forEach { $0.removeFromSuperlayer() }

let bezierPath = UIBezierPath()

bezierPath.addArc(withCenter: CGPoint(x: rect.midX, y: rect.midY), radius: rect.midX - ((lineWidth - 1) / 2), startAngle: -.pi / 2, endAngle: ((.pi * 2) * value) - (.pi / 2), clockwise: true)
bezierPath.addArc(withCenter: CGPoint(x: rect.midX, y: rect.midY),
radius: rect.midX - ((lineWidth) / 2),
startAngle: -.pi / 2,
endAngle: ((.pi * 2) * value) - (.pi / 2),
clockwise: true)

let shapeLayer = CAShapeLayer()

shapeLayer.path = bezierPath.cgPath
shapeLayer.lineCap = .square
shapeLayer.lineCap = .round

shapeLayer.strokeColor = circleTintColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor

Choose a reason for hiding this comment

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

이 코드 패치에 대해 간단한 코드 리뷰를 도와드리겠습니다.

  1. circleTintColor 변수의 초기값을 .clear로 변경하여 원의 틴트 색상을 투명하게 설정할 수 있게 되었습니다.
  2. draw(_ rect: CGRect) 함수에서 원 아크를 그리기 위한 베지어 패스 작성시, lineWidth 값을 조정하여 좀 더 정확한 그림을 그릴 수 있도록 되었습니다.
  3. draw(_ rect: CGRect) 함수내에서 subviews와 sublayers를 삭제하는 부분(self.subviews.forEach { $0.removeFromSuperview() }self.layer.sublayers?.forEach { $0.removeFromSuperlayer() })이 추가되었습니다. 이를 통해 이전에 그려진 원과 진행바를 지우고 다시 그릴 수 있게 되었습니다.
  4. 원모양 진행바(shapeLayer)의 lineCap 속성이 .square에서 .round으로 변경되었습니다. 이로 인해 진행바가 시작과 끝 지점에서 둥글게 그려집니다.

개선사항:

  • 코드는 큰 문제가 없어 보이지만, CircleProgressbar 클래스의 일부 속성들 (lineWidth, circleBackgroundColor, circleTintColor)에 대한 값의 범위(유효한 값)나 검증(예외 처리)이 필요한지 확인할 필요가 있습니다.
  • value가 nil인 경우에도 진행바를 그릴 수 있는 처리나 디폴트 값 설정 등이 추가될 수 있습니다.

위의 몇 가지 사항을 확인하여 속성 값의 유효성을 보장하고, 명확한 동작을 보장할 수 있도록 수정하는 것이 좋습니다.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@

import UIKit

class MyPageCommentViewController: UIViewController {
import SnapKit

final class MyPageCommentViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

self.view.backgroundColor = .donWhite
setUI()
setLayout()
}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
private func setUI() {
self.view.backgroundColor = .donPale
}

private func setLayout() {

}
*/

}

Choose a reason for hiding this comment

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

아래는 코드 패치입니다. 코드 리뷰를 짧게 도와드리겠습니다. 버그 위험과/또는 개선 제안이 있는지 확인해주세요.

❌ "import SnapKit" 라인이 @@ -7,23 +7,22 @@ 와 함께 추가되어 코드가 병합된 것으로 보입니다. 따라서 코드 조각 전체가 제공되지 않았으며 누락된 부분을 파악하기 어렵습니다.
❌ setLayout() 함수가 아직 구현되지 않은 것으로 보입니다. 이 함수를 구현하여 뷰의 레이아웃을 설정해야 합니다.
✅ viewDidLoad() 메서드 내에서 setUI()와 setLayout() 함수를 호출하여 UI 요소 설정 및 레이아웃 설정을 분리하는 접근 방식은 좋습니다.
✅ setUI() 함수 내에서 "self.view.backgroundColor = .donPale"로 배경색을 설정하는 것은 올바른 접근입니다.

요약:

  • "import SnapKit"을 추가해야 합니다.
  • setLayout() 함수를 구현해야 합니다.
  • setUI() 함수가 호출될 때 "self.view.backgroundColor = .donPale"와 같이 배경색이 설정됩니다.

Choose a reason for hiding this comment

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

이 코드 패치에 대한 간단한 코드 리뷰를 도와드리겠습니다. 버그 위험과/또는 개선 제안을 환영합니다.

주어진 코드 패치는 SnapKit 라이브러리를 추가로 임포트하고 UI 구성과 레이아웃 설정하는 데 사용되는 메서드가 추가되었습니다.

버그 위험:

  • 현재 주어진 코드 패치에는 명확한 버그 위험이 보이지 않습니다.

개선 제안:

  • setUI() 메서드에서 self.view.backgroundColor를 변경하는 논리적 단계가 추가되었습니다. 이로 인해 코드의 가독성과 유지 보수성이 향상될 수 있습니다.
  • setLayout() 메서드는 아직 구현되지 않았으므로 해당 메서드를 구현하여 레이아웃 설정에 필요한 코드를 추가해야 합니다.

일반적으로 코드 리뷰는 더 많은 상황과 코드 내용을 고려해야하지만, 주어진 정보로는 일부 개선 제안 및 버그 위험을 알려드릴 수 있었습니다.

Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,224 @@

import UIKit

class MyPageContentViewController: UIViewController {
import SnapKit

final class MyPageContentViewController: UIViewController {

// MARK: - Properties

var tabBarHeight: CGFloat = 0
var showUploadToastView: Bool = false
var deleteBottomsheet = DontBeBottomSheetView(singleButtonImage: ImageLiterals.Posting.btnDelete)
private let refreshControl = UIRefreshControl()

// MARK: - UI Components

lazy var homeCollectionView = HomeCollectionView().collectionView
private let uploadToastView = DontBeToastView()

private let transparentButtonPopupView = DontBePopupView(popupImage: ImageLiterals.Popup.transparentButtonImage,
popupTitle: StringLiterals.Home.transparentPopupTitleLabel,
popupContent: StringLiterals.Home.transparentPopupContentLabel,
leftButtonTitle: StringLiterals.Home.transparentPopupLefteftButtonTitle,
rightButtonTitle: StringLiterals.Home.transparentPopupRightButtonTitle)

// MARK: - Life Cycles

override func viewDidLoad() {
super.viewDidLoad()

getAPI()
setUI()
setHierarchy()
setLayout()
setDelegate()
setNotification()
setRefreshControll()
}

// MARK: - TabBar Height

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

tabBarHeight = tabBarController?.tabBar.frame.size.height ?? 0
}
}

self.view.backgroundColor = .donWhite
// MARK: - Extensions

extension MyPageContentViewController {
private func setUI() {
self.view.backgroundColor = UIColor.donGray1
self.navigationController?.navigationBar.isHidden = true
uploadToastView.alpha = 0
transparentButtonPopupView.alpha = 0
}

private func setHierarchy() {
view.addSubviews(homeCollectionView,
uploadToastView,
transparentButtonPopupView)
}

private func setLayout() {
homeCollectionView.snp.makeConstraints {
$0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
$0.leading.trailing.bottom.equalToSuperview()
$0.width.equalTo(UIScreen.main.bounds.width)
}

uploadToastView.snp.makeConstraints {
$0.leading.trailing.equalToSuperview().inset(16.adjusted)
$0.bottom.equalTo(tabBarHeight).inset(6.adjusted)
$0.height.equalTo(44)
}

transparentButtonPopupView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}

private func setDelegate() {
homeCollectionView.dataSource = self
homeCollectionView.delegate = self
transparentButtonPopupView.delegate = self
}

private func setNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(showToast(_:)), name: WriteViewController.showUploadToastNotification, object: nil)
}

private func setRefreshControll() {
refreshControl.addTarget(self, action: #selector(refreshPost), for: .valueChanged)
homeCollectionView.refreshControl = refreshControl
refreshControl.backgroundColor = .donGray1
}

@objc
func refreshPost() {
DispatchQueue.main.async {
// ✅ 서버 통신 영역
//
}
self.homeCollectionView.reloadData()
self.perform(#selector(self.finishedRefreshing), with: nil, afterDelay: 0.1)
}

@objc
func finishedRefreshing() {
refreshControl.endRefreshing()
}



@objc func showToast(_ notification: Notification) {
if let showToast = notification.userInfo?["showToast"] as? Bool {
if showToast == true {
uploadToastView.alpha = 1

var value: Double = 0.0
let duration: TimeInterval = 1.0 // 애니메이션 기간 (초 단위)
let increment: Double = 0.01 // 증가량

// 0에서 1까지 1초 동안 0.01씩 증가하는 애니메이션 블록
UIView.animate(withDuration: duration, delay: 0.0, options: .curveLinear, animations: {
for i in 1...100 {
DispatchQueue.main.asyncAfter(deadline: .now() + (duration / 100) * TimeInterval(i)) {
value = Double(i) * increment
self.uploadToastView.circleProgressBar.value = value
}
}
})

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.uploadToastView.circleProgressBar.alpha = 0
self.uploadToastView.checkImageView.alpha = 1
self.uploadToastView.toastLabel.text = StringLiterals.Toast.uploaded
self.uploadToastView.container.backgroundColor = .donPrimary
}

UIView.animate(withDuration: 1.0, delay: 3, options: .curveEaseIn) {
self.uploadToastView.alpha = 0
}

DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
self.uploadToastView.circleProgressBar.alpha = 1
self.uploadToastView.checkImageView.alpha = 0
self.uploadToastView.toastLabel.text = StringLiterals.Toast.uploading
self.uploadToastView.container.backgroundColor = .donGray3
}
}
}
}
}

// MARK: - Network

/*
// MARK: - Navigation
extension MyPageContentViewController {
private func getAPI() {

}
}

extension MyPageContentViewController: UICollectionViewDelegate { }

extension MyPageContentViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell =
HomeCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
cell.KebabButtonAction = {
self.deleteBottomsheet.showSettings()
}
cell.LikeButtonAction = {
cell.isLiked.toggle()
cell.likeButton.setImage(cell.isLiked ? ImageLiterals.Posting.btnFavoriteActive : ImageLiterals.Posting.btnFavoriteInActive, for: .normal)
}
cell.TransparentButtonAction = {
self.transparentButtonPopupView.alpha = 1
}
return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 343.adjusted, height: 210.adjusted)
}
Comment on lines +194 to +196
Copy link
Collaborator

Choose a reason for hiding this comment

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

P1
이렇게 쓰면 dynamic height 작동 안 됩니다 ! 추후 수정 고려해주세요 :>


func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
guard let footer = homeCollectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "HomeCollectionFooterView", for: indexPath) as? HomeCollectionFooterView else { return UICollectionReusableView() }
return footer
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {

return CGSize(width: UIScreen.main.bounds.width, height: 24.adjusted)
}
}

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
extension MyPageContentViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let yOffset = scrollView.contentOffset.y
let navigationBarHeight = self.navigationController?.navigationBar.frame.height ?? 0
if yOffset > 0 {
scrollView.isScrollEnabled = true
} else if yOffset < 0 {
scrollView.isScrollEnabled = false
}
}
*/
}

extension MyPageContentViewController: DontBePopupDelegate {
func cancleButtonTapped() {
transparentButtonPopupView.alpha = 0
}

func confirmButtonTapped() {
transparentButtonPopupView.alpha = 0
// ✅ 투명도 주기 버튼 클릭 시 액션 추가
}
}

Choose a reason for hiding this comment

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

코드 리뷰:

  1. viewDidLayoutSubviews 메서드에서 tabBarHeight를 계산하고 있지만, 이 메서드는 레이아웃 갱신시마다 호출되므로 많은 메모리 할당을 유발할 수 있습니다. 대신 viewWillAppear 등의 메서드에서 한 번만 계산하면 됩니다.

  2. getAPI() 메서드가 비어있어서 기능을 파악하기 힘듭니다. 해당 메서드에 적절한 네트워크 요청 코드나 관련 작업을 추가해야 합니다.

  3. view.backgroundColornavigationController?.navigationBar.isHidden에서 사용되는 UIColor 확장을 제거해도 됩니다. "UIColor.donGray1"와 같은 값 자체를 사용해도 괜찮습니다.

  4. homeCollectionViewsnp.makeConstraints 부분에서 safeAreaLayoutGuide 대신 self.view를 사용하는 것이 좋습니다.

  5. setDelegate 메서드는 데이터 소스 설정을 누락하고 있습니다. homeCollectionView.dataSource = self를 추가해야 합니다.

  6. showToast(_:notification:) 메서드가 복잡하게 작성되어 있습니다. 애니메이션과 딜레이 로직을 조금 더 개선하고, GCD(dispatch)를 사용하여 적절한 스레드에서 실행되도록 수정할 수 있습니다.

  7. collectionView(_:cellForItemAt:) 메서드에서 HomeCollectionViewCell.dequeueReusableCell 메서드 호출이 여전히 누락되어 있습니다. 실제로 셀을 반환하기 위해 이를 추가해야 합니다.

  8. scrollViewDidScroll(_:) 메서드에는 스크롤 동작과 관련된 추가적인 로직도 필요합니다. 현재는 스크롤이 아래로 되면 스크롤이 가능하도록 처리되고, 위로 갈 때는 스크롤이 불가능하게 처리되고 있습니다.

  9. DontBePopupDelegate 프로토콜에서 "투명도 주기" 버튼 클릭 시 액션 추가가 필요한 부분에 대해 미구현 상태입니다.

  10. 코드 내의 일부 한글 주석이 보이지만, 문제 없이 작동할 수 있습니다. 다만 주변 코드와 통일성을 위해 한국어 주석을 모두 영문 주석으로 변경하는 것을 고려할 수 있습니다.

  11. 축약 표현을 사용하여 코드를 좀더 읽기 쉽게 만들 수 있습니다.

  12. 버그나 리스크 요소는 일목요연하게 확인할 수 없습니다. 전체 소스 코드와 해당 코드를 사용하는 다른 장소를 파악해야 완전한 평가 및 리뷰를 할 수 있습니다.

Loading