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

레시피 상세화면 UI를 정의해 보았습니다. #12

Merged
merged 29 commits into from
Jul 11, 2024

Conversation

GeonH0
Copy link
Collaborator

@GeonH0 GeonH0 commented Jul 3, 2024

  • 다른 화면면에도 사용할 NaviagtionBar를 정의해 보았습니다.
  • Mapper의 이름을 RecipeItem에서 Detail로 변경했습니다.
  • DetailViewController를 정의했습니다.
  • View를 정의했습니다.

let name: String
let description: String
let imageUrls: [URL]
let isLikedByCurrentUser: Bool

Choose a reason for hiding this comment

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

뷰모델에서는 좀 더 UI에 맞는 네이밍으로 가는건 어때요?
도메인에서의 디테일한 이름을 그대로 담기보다는 isLiked 정도로 충분해보여요.

Copy link
Collaborator Author

@GeonH0 GeonH0 Jul 4, 2024

Choose a reason for hiding this comment

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

[9881443],[fa15e8f] 수정했습니다

Choose a reason for hiding this comment

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

앞에 다 대문자로 시작하는데 카멜케이스로 고쳐주시면 좋을것같아요

let description: String
let imageUrls: [URL]
let isLikedByCurrentUser: Bool
let createdAt: Date

Choose a reason for hiding this comment

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

이 값은 뷰에서 어떻게 쓰이는거에요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

description 은 설명
imgarUrls는 레시피의 이미지
isLikedByCurrentUser는 추후만들수도 있는 좋아요 유무
createAt는 현재는 사용하고 있지 않고 있어요

Choose a reason for hiding this comment

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

사용하지 않는건 제거해주세요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[f8b8c75] 수정했습니다

Comment on lines 8 to 9
import UIKit
import Kingfisher

Choose a reason for hiding this comment

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

써드파티와 퍼스트 파티 사이는 한 줄 띄어주세요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[d508f7e] 수정했습니다


private let scrollView = UIScrollView()
private let pageControl = UIPageControl()
private let recipenameLabel = UILabel()

Choose a reason for hiding this comment

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

Suggested change
private let recipenameLabel = UILabel()
private let recipeNameLabel = UILabel()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[092afac] 수정했습니다

private let scrollView = UIScrollView()
private let pageControl = UIPageControl()
private let recipenameLabel = UILabel()
private let recipedescriptionLabel = UILabel()

Choose a reason for hiding this comment

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

Suggested change
private let recipedescriptionLabel = UILabel()
private let recipeDescriptionLabel = UILabel()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[1b83f41] 수정했습니다

private let recipenameLabel = UILabel()
private let recipedescriptionLabel = UILabel()
private let photoIndexLabel = UILabel()
private var recipeimageUrls: [URL] = []

Choose a reason for hiding this comment

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

Suggested change
private var recipeimageUrls: [URL] = []
private var recipeImageUrls: [URL] = []

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[a78dd53] 수정했습니다

recipedescriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20)
])

recipenameLabel.font = .systemFont(ofSize: 24, weight: .bold)

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[1f1c396]
적용 시켰는데 혹시 안올라 간건가요?

Copy link

@f-lab-barry f-lab-barry Jul 5, 2024

Choose a reason for hiding this comment

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

뒤 커밋에서 적용하신거 확인했어요~ 이런 부분에 fixup이나 squash로 커밋을 정리해서 올리면 좋겠네요 😄
(대부분 커밋 순서대로 리뷰한다는 것을 감안해주세요. file Changes로 보면 수정이 되어있겠지만 커밋단위는 되어있지 않아요.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 다음부터는 커밋 정리후 올리겠습니다!

func configure(with viewModel: RecipeDetailViewModel) {
recipenameLabel.text = viewModel.name
recipedescriptionLabel.text = viewModel.description
recipeimageUrls = viewModel.imageUrls

Choose a reason for hiding this comment

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

recipeimageUrls를 내부에 들고있는 이유가 따로 있을까요?
스크롤뷰를 구성하는데 바로 전달해서 사용해도 괜찮지 않을까해서요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

View에서 들고 있고 스크롤뷰로 등록하는게 좀 더 안전하다 생각해서 내부에 들고 있었는데 수정하겠습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[18901d0] 수정했습니다

Choose a reason for hiding this comment

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

뷰에서 들고있고 등록하는게 좀 더 안전하다는 게 어떤 의미일까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

View 내부에서 recipeimageUrls 데이터를 들고 ScrollView에 등록하는게 좀 더 안전하다고 생각했었습니다!

Choose a reason for hiding this comment

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

저는 굳이 캐싱할 이유가 안느껴져서 어떤 케이스에서 안정적이지 않은 부분이 발생해서 조치하신건지 궁금했었던거였어요!
일단 수정해주신 부분은 확인했습니다.

Choose a reason for hiding this comment

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

@GeonH0 UI 단에서는 데이터 캐싱을 최대한 하지 않고 뷰를 멍청하게 만드는 것이 중요해요.
캐싱할 일이 있다면 꼭 필요한지 고민해주시면 좋을 것 같아요~
왜 뷰가 멍청해야하는지는 찾아봐주시면 재밌을거에요! ㅎㅎ

private func setupScrollView() {
let imageViewWidth = UIScreen.main.bounds.width

for (index, url) in recipeimageUrls.enumerated() {

Choose a reason for hiding this comment

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

전체를 다 돌리는 거라면 for - in 보다 recipeimageUrls.enumerated().forEach를 쓰는 것에 대해 어떻게 생각하시나요?
개인적으로 앞부붙에 반복문을 돌리는 프로퍼티가 보여서 가독성이 좋다고 생각하는데 건호님 생각도 궁금하네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

가독성 측면에서 forEach가 훨씬 좋아보이네요 수정하겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[75ce3f8] 수정했습니다


let xPos = CGFloat(index) * imageViewWidth
imageView.frame = CGRect(x: xPos, y: 0, width: imageViewWidth, height: 200)
scrollView.addSubview(imageView)

Choose a reason for hiding this comment

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

이렇게 스크롤뷰를 셋업할 때마다 이미지뷰를 추가해주는 거라면
setupScrollView() 가 의도치 않게 두번 호출되면 어떻게 되는걸까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그 부분을 생각 못한거 같습니다. 이미지뷰가 추가 되었는지 플래그를 사용하여 방지하게 수정하도록 하겠습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[efc1c64] 수정했습니다

Choose a reason for hiding this comment

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

두번째 호출 된 것이 더 올바른 이미지일 수 있지 않을까요?
첫 진입을 막는 것보다 몇번을 호출되더라도 안정적으로 최종적으로 등록한 이미지가 보이도록
기존에 등록한 이미지를 제거한 후(removeFromSuperView) addSubview를 다시 호출해줘야할 것 같다는 의견이었어요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

기존에 등록한 이미지를 제거후 다시 호출하는 메서드로 변경시키겠습니다

Choose a reason for hiding this comment

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

확인했습니다~

}

private func setupScrollView() {
let imageViewWidth = UIScreen.main.bounds.width

Choose a reason for hiding this comment

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

화면을 꽉 채우고 싶으신 것 같은데 UIScreen.main에 접근하는 방법 말고는 없을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

self.bounds.width 를 사용하는 방법도 있을거 같아요!

let imageView = UIImageView()
imageView.kf.setImage(with: url)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true

Choose a reason for hiding this comment

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

clipsToBounds는 왜 true로 바꾸신 거에요? (필요한 지점을 찾지 못해서 질문해요)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이전에 이미지 화면에서 양 옆 공백을 만들었다가 지우면서 제거 하지 못한 코드 같아요 제거하겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[4830cf5] 제거완료했습니다

Comment on lines 47 to 66
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
scrollView.heightAnchor.constraint(equalToConstant: 200),

pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 10),
pageControl.centerXAnchor.constraint(equalTo: centerXAnchor),

photoIndexLabel.topAnchor.constraint(equalTo: pageControl.bottomAnchor, constant: 10),
photoIndexLabel.centerXAnchor.constraint(equalTo: centerXAnchor),

recipenameLabel.topAnchor.constraint(equalTo: photoIndexLabel.bottomAnchor, constant: 20),
recipenameLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
recipenameLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),

recipedescriptionLabel.topAnchor.constraint(equalTo: recipenameLabel.bottomAnchor, constant: 20),
recipedescriptionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
recipedescriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20)
])

Choose a reason for hiding this comment

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

해당 setupUI 메서드가 여러 역할이 있어보이고 상당히 뚱뚱해진 것 같아요. 이부분은 레이아웃을 정의하는 메서드로 분리하는 등으로 개선해보면 어떨까요? (다른 방법으로 분리해주셔도 좋아요)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

layout,labels,scroolview,pagecontrol 등으로 분리해보겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[0832b39] 수정했습니다

Copy link

@f-lab-barry f-lab-barry Jul 5, 2024

Choose a reason for hiding this comment

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

메서드 분리 관점에 대해 더 고민해보시면 좋을 것 같아요
개인적으로 뷰의 addSubview는 한번에 보이는 것이 뷰의 구조를 파악하는데 이점이 있어보여요
어떤 것을 모으고, 모은 것 가운데 어떤걸 메서드로 추출해 분리하는 것이 좋을지 고민해주세요! 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 좀 더 고민하고 수정해 보겠습니다


extension RecipeDetailView: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x / UIScreen.main.bounds.width)

Choose a reason for hiding this comment

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

반올림하면 페이지 수 계산에 문제 없나요?

Copy link
Collaborator Author

@GeonH0 GeonH0 Jul 4, 2024

Choose a reason for hiding this comment

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

이미지를 반 이상 넘기면 바로바로 변하기 위해 넣은 건데 페이지 수 계산에는 문제가 없는데 제거 하는게 좋을까요?

Choose a reason for hiding this comment

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

의도하신거라면 괜찮습니다!


final class RecipeDetailViewController: UIViewController {

private let recipeDetailView = RecipeDetailView()

Choose a reason for hiding this comment

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

Suggested change
private let recipeDetailView = RecipeDetailView()
private let contentView = RecipeDetailView()

뷰컨에서는 contentView로 들고있는게 좋아보여요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[60fa4e2] 수정했습니다

Comment on lines 8 to 9
import UIKit
import RxSwift

Choose a reason for hiding this comment

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

써드파티 사이의 개행 부탁드려요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[61a0df9] 수정했습니다

private let recipeDetailView = RecipeDetailView()
private let customNavigationBar = CustomNavigationBar()
private let interactor: RecipeDetailInteractor
private let disposeBag = DisposeBag()

Choose a reason for hiding this comment

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

disposeBag을 let으로 들고있어도 문제 없나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

disposeBag이 일회용 구독이라 괜찮을거 같아요

Choose a reason for hiding this comment

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

취소는 필요하지 않나보군요 넵!


private func setupUI() {
view.backgroundColor = .white
view.addSubview(recipeDetailView)

Choose a reason for hiding this comment

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

addSubview 보다 view = contentView를 해주는게 더 낫지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[690680d] 수정했습니다

private func setupUI() {
view.backgroundColor = .white
view.addSubview(recipeDetailView)
view.addSubview(customNavigationBar)

Choose a reason for hiding this comment

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

상단 네비바를 add 해주는 방향인가요?

Copy link
Collaborator Author

@GeonH0 GeonH0 Jul 4, 2024

Choose a reason for hiding this comment

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

네 상단 네비바를 add 해서 사용하려고 했습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[690680d] 상단 네비바를 View로 이동시키고 행동만 ViewController에 정의했습니다

}

// MARK: - RecipeDetailInteractorDelegate
extension RecipeDetailViewController: RecipeDetailInteractorDelegate {

Choose a reason for hiding this comment

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

mark 주석 아래 한 줄 개행 해주세요~

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[beca64c] 수정했습니다

func fetchedRecipe(result: Result<Recipe, Error>) {
switch result {
case .success(let recipe):
let recipeItemViewModel = RecipeListMapper().mapToRecipeDetailViewModel(from: recipe)

Choose a reason for hiding this comment

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

mapper를 여기서 바로 생성해서 쓰면 추후에 사용할 일이 또 생겼을 때 매번 생성하는건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

재 사용성 측면을 전혀 고려하지 않은거 같아요 mapper 인스턴스를 선언후 재사용하도록 수정하겠습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[1c0bb26] 수정했습니다

Comment on lines 75 to 77
DispatchQueue.main.async {
self.recipeDetailView.configure(with: recipeItemViewModel)
}

Choose a reason for hiding this comment

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

여기서 비동기가 필요한 이유가 뭘까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

configure를 통해서 UI 를 업데이트 시키기 때문에 필요하다고 판단했습니다

Choose a reason for hiding this comment

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

없는것과 있는 것 쓰레드 테스트 부탁드려요.

Comment on lines 79 to 81
DispatchQueue.main.async {
self.displayError(error)
}

Choose a reason for hiding this comment

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

여기도요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 부분은 제거 하겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[38f28b5] 제거했습니다

@@ -10,4 +10,6 @@ import UIKit
struct Fonts {
static let titleFont: UIFont = .systemFont(ofSize: 16, weight: .bold)
static let bodyFont: UIFont = .systemFont(ofSize: 14, weight: .regular)
static let DetailtitleFont: UIFont = .systemFont(ofSize: 24, weight: .bold)

Choose a reason for hiding this comment

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

Suggested change
static let DetailtitleFont: UIFont = .systemFont(ofSize: 24, weight: .bold)
static let DetailTitleFont: UIFont = .systemFont(ofSize: 24, weight: .bold)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[e3b3ad9] 변경했습니다

Choose a reason for hiding this comment

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

맨 앞 대문자도 변경해주세요. 아래 디스크립션 폰트도 대문자로 되어있네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[f8b8c75]적용했습니다

DispatchQueue.main.async {
self.displayError(error)
}
self.displayError(error)

Choose a reason for hiding this comment

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

요거 앞에 인덴트가 많이 들어갔어요~

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[a82aabd] 수정했습니다!

Copy link

@f-lab-barry f-lab-barry left a comment

Choose a reason for hiding this comment

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

인덴트만 챙겨주세요. 전체적으로 확인은 했어요

@GeonH0 GeonH0 force-pushed the feature/FeedDetailView branch from b89b6bf to 557a785 Compare July 9, 2024 08:11
Copy link

@GeonH0 GeonH0 merged commit af2a046 into main Jul 11, 2024
2 checks passed
@GeonH0 GeonH0 deleted the feature/FeedDetailView branch September 12, 2024 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants