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를 생성해보았습니다. #9

Merged
merged 36 commits into from
Jul 2, 2024

Conversation

GeonH0
Copy link
Collaborator

@GeonH0 GeonH0 commented Jun 19, 2024

||

  • 레시피 제목을 검색 하기 위한 SearchBar를 생성했습니다.
  • 각 Cell을 보여주는 ListView를 정의하였습니다.
  • 각 레시피의 대표사진과 제목을 보여주는 Cell을 정의하였습니다.
  • ViewModel에서 받아온 데이터를 기반으로 레시피 리스트의 행동들을 ViewController에 정의 했습니다.

@f-lab-barry
Copy link

  • 레시피 제목을 검색 하기 위한 SearchBar를 생성했습니다.
  • 각 Cell을 보여주는 ListView를 정의하였습니다.
  • 각 레시피의 대표사진과 제목을 보여주는 Cell을 정의하였습니다.
  • ViewModel에서 받아온 데이터를 기반으로 레시피 리스트의 행동들을 ViewController에 정의 했습니다.

UI 개발 작업은 스크린샷도 함께 넣어주시면 이해가 더 잘 될 것 같아요.


import UIKit

class SearchBar: UIView {

Choose a reason for hiding this comment

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

UI는 확장할 일이 없으니 final class로 작성 부탁드려요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[145bd41] 변경 했습니다

private let searchBar = UISearchBar()

var searchText: String? {
return searchBar.text

Choose a reason for hiding this comment

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

return 이 없어도 될 것 같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[db5e2ef] 제거했습니다


import UIKit

class RecipeListView: UIView {

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.

[7a3ba89] 변경했습니다


class RecipeListView: UIView {

let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

Choose a reason for hiding this comment

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

collectionView를 internal로 선언한 이유가 있을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ViewController에서
recipelistView.collectionView.dataSource = self
recipelistView.collectionView.delegate = self
이런식으로 선언하다보니 캡슐화를 놓친거 같습니다 변경하겠습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[22ee02b] 수정했습니다

Choose a reason for hiding this comment

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

delegate와 dataSource는 view에서 같이 구현

Choose a reason for hiding this comment

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

@GeonH0 이 부분은 아직 수정전이신가요~?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@f-lab-barry 내일 오전 중으로 수정 커밋 올리겠습니다!

Copy link
Collaborator Author

@GeonH0 GeonH0 Jul 1, 2024

Choose a reason for hiding this comment

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

[7077713],[c1bc9a0],[f0115b8] 수정했습니다


private func configureCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: UIScreen.main.bounds.width - 20, height: 200)

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.

[2be84eb] 변경했습니다


import UIKit

class RecipeListViewCell: UICollectionViewCell {

Choose a reason for hiding this comment

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

여기도 final 챙겨주세요~

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[db38695] 변경했습니다


class RecipeListViewCell: UICollectionViewCell {

private let imageView = UIImageView()

Choose a reason for hiding this comment

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

네이밍이 좀 더 구체적이면 어떨까요? (예. recipeThumbnailView)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[ee35fb9] 변경했습니다

imageView.topAnchor.constraint(equalTo: topAnchor),
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
imageView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.75),

Choose a reason for hiding this comment

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

75%면 3:4 사진 비율인건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

네 3:4입니다

titleLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.15)
])

titleLabel.font = .systemFont(ofSize: 16, 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

@GeonH0 GeonH0 Jun 25, 2024

Choose a reason for hiding this comment

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

[34985f7],[24d0856] 변경했습니다

Comment on lines 53 to 54
imageView.image = nil
}

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.

[76fdeba],[e1a3fc2] 기본이미지를 추가했습니다

Comment on lines 56 to 57
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.

이건 한번만 선언해주면 되는 것 같은데 configure 메서드에 있기보다 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.

[24a71e2] 변경했습니다

Comment on lines 60 to 67
private func loadImage(from url: URL) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}.resume()
}

Choose a reason for hiding this comment

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

ImageView 에 extension 으로 빠져있으면 다른 화면에서 재사용이 가능한 로직으로 보이는데
재사용할 수 있도록 빼놓는 건 어떻게 생각하시나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[72e9fe8],[cf0c78c] 변경했습니다


import UIKit

class RecipeListViewController: UIViewController, RecipeListViewModelDelegate {

Choose a reason for hiding this comment

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

여기도 final 챙겨주세요!

Choose a reason for hiding this comment

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

RecipeListViewModelDelegate는 extention RecipeListViewController 으로 확장해서 가독성을 높이면 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[15f80f0],[833b474] 변경했습니다

extension RecipeListViewController: UISearchBarDelegate {

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text?.count == 0 {

Choose a reason for hiding this comment

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

String의 extenstion에 isBlank 프러퍼티를 추가해 trim 하고 비어있는지 검토하는 로직을 넣어보는건 어때요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[be060c4],[99c274e] 변경했습니다

Choose a reason for hiding this comment

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

코드에 이상은 없지만 불필요한 return 빼주시는게 swift 컨벤션에 더 맞을 것 같아요~

Copy link
Collaborator Author

@GeonH0 GeonH0 Jun 29, 2024

Choose a reason for hiding this comment

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

[3c03347] 제거 했습니다!

private var viewModel: RecipeListViewModel
private var recipes: [RecipeListItemViewModel] = []
private let searchBar = UISearchBar()
private let recipelistView = RecipeListView()

Choose a reason for hiding this comment

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

Suggested change
private let recipelistView = RecipeListView()
private let recipeListView = RecipeListView()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[6edb8bb] 변경했습니다


private var viewModel: RecipeListViewModel
private var recipes: [RecipeListItemViewModel] = []
private let searchBar = UISearchBar()

Choose a reason for hiding this comment

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

아까 첫 커밋에서 만든 SearchBar는 어디에 쓰는거에요? 여기서 UISearchBar를 쓰고있길래 여쭤봐요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[2a6372c] 수정했습니다

searchBar.delegate = self
}

func didFetchRecipes(_ recipes: [RecipeListItemViewModel]) {

Choose a reason for hiding this comment

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

fetchedRecipes 어떤가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[c0bd5a0] 변경했습니다

Comment on lines 61 to 64
DispatchQueue.main.async {
self.recipes = recipes
self.recipelistView.collectionView.reloadData()
}

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.

UI를 업데이트 시키기 위해 비동기 처리가 필요하다고 판단했습니다.

Comment on lines 67 to 69
func didFail(with error: Error) {
print("Error: \(error.localizedDescription)")
}

Choose a reason for hiding this comment

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

fetch에 실패할 때 호출하는 메서드라면 Result 타입으로 다루던걸 그대로 전달하면서 에러처리하면 어때요?
메서드를 두개로 나눌 필요가 없어보여서요!

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 fetchedRecipes(result: Result<[RecipeListItemViewModel], Error>) {
    DispatchQueue.main.async {
        switch result {
        case .success(let recipes):
            self.recipes = recipes
            self.recipeListView.reloadCollectionViewData()
        case .failure(let error):
            print("Error: \(error.localizedDescription)")
        }
    }
}
이런식으로 그대로 전달하면서 에러처리 말씀하시는거 맞나요?

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.

[4eb8154],[e57455d] 수정했습니다


extension RecipeListViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return recipes.count

Choose a reason for hiding this comment

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

섹션은 1개 아닌가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

각 섹션에는 recipes의 갯수 만큼 반환해야 된다 생각했습니다

navigationController?.pushViewController(detailVC, animated: true)
} else {
let RecipeIDErrorAlert = UIAlertController(title: "오류", message: "해당 정보를 찾지 못했습니다.", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { _ in

Choose a reason for hiding this comment

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

핸들러에 [weak self] 필요하지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[52d89ea] 수정했습니다

@@ -9,6 +9,7 @@ import UIKit

final class RecipeListView: UIView {

private let itemSize = CGSize(width: UIScreen.main.bounds.width - 20, height: 200)
Copy link

@f-lab-barry f-lab-barry Jun 25, 2024

Choose a reason for hiding this comment

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

private enum Metric {
  static let itemSize: CGSize = .init(width: UIScreen.main.bounds.width - 20, height: 200)
  static let topMargin: CGFloat = 10.0
}

Metrice.itemSize

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[1a760ca] 적용했습니다

@@ -119,3 +108,17 @@ extension RecipeListViewController: UISearchBarDelegate {
interactor.searchRecipes(with: query)
}
}

Choose a reason for hiding this comment

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

// MARK: - RecipeListViewModelDelegate

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

[d3dcc4e] 적용했습니다!

@GeonH0 GeonH0 force-pushed the feature/feedListView branch from a485b9b to 3c03347 Compare June 29, 2024 14:47
Copy link

sonarqubecloud bot commented Jul 1, 2024

@GeonH0 GeonH0 merged commit c7b4324 into main Jul 2, 2024
2 checks passed
@GeonH0 GeonH0 deleted the feature/feedListView branch September 12, 2024 00:59
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