-
Notifications
You must be signed in to change notification settings - Fork 0
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
Conversation
GeonH0
commented
Jul 3, 2024
- 다른 화면면에도 사용할 NaviagtionBar를 정의해 보았습니다.
- Mapper의 이름을 RecipeItem에서 Detail로 변경했습니다.
- DetailViewController를 정의했습니다.
- View를 정의했습니다.
let name: String | ||
let description: String | ||
let imageUrls: [URL] | ||
let isLikedByCurrentUser: Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뷰모델에서는 좀 더 UI에 맞는 네이밍으로 가는건 어때요?
도메인에서의 디테일한 이름을 그대로 담기보다는 isLiked 정도로 충분해보여요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 값은 뷰에서 어떻게 쓰이는거에요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description 은 설명
imgarUrls는 레시피의 이미지
isLikedByCurrentUser는 추후만들수도 있는 좋아요 유무
createAt는 현재는 사용하고 있지 않고 있어요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용하지 않는건 제거해주세요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[f8b8c75] 수정했습니다
import UIKit | ||
import Kingfisher |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
써드파티와 퍼스트 파티 사이는 한 줄 띄어주세요
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private let recipenameLabel = UILabel() | |
private let recipeNameLabel = UILabel() |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private let recipedescriptionLabel = UILabel() | |
private let recipeDescriptionLabel = UILabel() |
There was a problem hiding this comment.
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] = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private var recipeimageUrls: [URL] = [] | |
private var recipeImageUrls: [URL] = [] |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저희 폰트 따로 정의해서 쓰기로 하지 않았나요?
https://github.com/f-lab-edu/HomeCafeRecipes/blob/main/HomeCafeRecipes/HomeCafeRecipes/Resources/Fonts.swift
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[1f1c396]
적용 시켰는데 혹시 안올라 간건가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뒤 커밋에서 적용하신거 확인했어요~ 이런 부분에 fixup이나 squash로 커밋을 정리해서 올리면 좋겠네요 😄
(대부분 커밋 순서대로 리뷰한다는 것을 감안해주세요. file Changes로 보면 수정이 되어있겠지만 커밋단위는 되어있지 않아요.)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recipeimageUrls를 내부에 들고있는 이유가 따로 있을까요?
스크롤뷰를 구성하는데 바로 전달해서 사용해도 괜찮지 않을까해서요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
View에서 들고 있고 스크롤뷰로 등록하는게 좀 더 안전하다 생각해서 내부에 들고 있었는데 수정하겠습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[18901d0] 수정했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뷰에서 들고있고 등록하는게 좀 더 안전하다는 게 어떤 의미일까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
View 내부에서 recipeimageUrls 데이터를 들고 ScrollView에 등록하는게 좀 더 안전하다고 생각했었습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 굳이 캐싱할 이유가 안느껴져서 어떤 케이스에서 안정적이지 않은 부분이 발생해서 조치하신건지 궁금했었던거였어요!
일단 수정해주신 부분은 확인했습니다.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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를 쓰는 것에 대해 어떻게 생각하시나요?
개인적으로 앞부붙에 반복문을 돌리는 프로퍼티가 보여서 가독성이 좋다고 생각하는데 건호님 생각도 궁금하네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
가독성 측면에서 forEach가 훨씬 좋아보이네요 수정하겠습니다!
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 스크롤뷰를 셋업할 때마다 이미지뷰를 추가해주는 거라면
setupScrollView() 가 의도치 않게 두번 호출되면 어떻게 되는걸까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그 부분을 생각 못한거 같습니다. 이미지뷰가 추가 되었는지 플래그를 사용하여 방지하게 수정하도록 하겠습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[efc1c64] 수정했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두번째 호출 된 것이 더 올바른 이미지일 수 있지 않을까요?
첫 진입을 막는 것보다 몇번을 호출되더라도 안정적으로 최종적으로 등록한 이미지가 보이도록
기존에 등록한 이미지를 제거한 후(removeFromSuperView) addSubview를 다시 호출해줘야할 것 같다는 의견이었어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존에 등록한 이미지를 제거후 다시 호출하는 메서드로 변경시키겠습니다
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
화면을 꽉 채우고 싶으신 것 같은데 UIScreen.main에 접근하는 방법 말고는 없을까요?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clipsToBounds는 왜 true로 바꾸신 거에요? (필요한 지점을 찾지 못해서 질문해요)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이전에 이미지 화면에서 양 옆 공백을 만들었다가 지우면서 제거 하지 못한 코드 같아요 제거하겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[4830cf5] 제거완료했습니다
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) | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 setupUI 메서드가 여러 역할이 있어보이고 상당히 뚱뚱해진 것 같아요. 이부분은 레이아웃을 정의하는 메서드로 분리하는 등으로 개선해보면 어떨까요? (다른 방법으로 분리해주셔도 좋아요)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
layout,labels,scroolview,pagecontrol 등으로 분리해보겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[0832b39] 수정했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메서드 분리 관점에 대해 더 고민해보시면 좋을 것 같아요
개인적으로 뷰의 addSubview는 한번에 보이는 것이 뷰의 구조를 파악하는데 이점이 있어보여요
어떤 것을 모으고, 모은 것 가운데 어떤걸 메서드로 추출해 분리하는 것이 좋을지 고민해주세요! 😄
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반올림하면 페이지 수 계산에 문제 없나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이미지를 반 이상 넘기면 바로바로 변하기 위해 넣은 건데 페이지 수 계산에는 문제가 없는데 제거 하는게 좋을까요?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private let recipeDetailView = RecipeDetailView() | |
private let contentView = RecipeDetailView() |
뷰컨에서는 contentView로 들고있는게 좋아보여요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[60fa4e2] 수정했습니다
import UIKit | ||
import RxSwift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
써드파티 사이의 개행 부탁드려요
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
disposeBag을 let으로 들고있어도 문제 없나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
disposeBag이 일회용 구독이라 괜찮을거 같아요
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addSubview 보다 view = contentView를 해주는게 더 낫지 않을까요?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상단 네비바를 add 해주는 방향인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네 상단 네비바를 add 해서 사용하려고 했습니다
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mark 주석 아래 한 줄 개행 해주세요~
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mapper를 여기서 바로 생성해서 쓰면 추후에 사용할 일이 또 생겼을 때 매번 생성하는건가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재 사용성 측면을 전혀 고려하지 않은거 같아요 mapper 인스턴스를 선언후 재사용하도록 수정하겠습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[1c0bb26] 수정했습니다
DispatchQueue.main.async { | ||
self.recipeDetailView.configure(with: recipeItemViewModel) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 비동기가 필요한 이유가 뭘까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
configure를 통해서 UI 를 업데이트 시키기 때문에 필요하다고 판단했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
없는것과 있는 것 쓰레드 테스트 부탁드려요.
DispatchQueue.main.async { | ||
self.displayError(error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기도요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 제거 하겠습니다!
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static let DetailtitleFont: UIFont = .systemFont(ofSize: 24, weight: .bold) | |
static let DetailTitleFont: UIFont = .systemFont(ofSize: 24, weight: .bold) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[e3b3ad9] 변경했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맨 앞 대문자도 변경해주세요. 아래 디스크립션 폰트도 대문자로 되어있네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[f8b8c75]적용했습니다
…ionBar를 RecipeDetailView로 이동
DispatchQueue.main.async { | ||
self.displayError(error) | ||
} | ||
self.displayError(error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거 앞에 인덴트가 많이 들어갔어요~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[a82aabd] 수정했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
인덴트만 챙겨주세요. 전체적으로 확인은 했어요
b89b6bf
to
557a785
Compare
Quality Gate passedIssues Measures |