- 주제에 맞는 사진을 볼수 있으며, 검색을 통하여 원하는 분류의 사진을 볼 수 있습니다.
Unsplash API
를 통하여 사진을 업로드 합니다
hamg |
---|
Github Profile |
메인 화면 | 테마 선택 화면 | |
---|---|---|
작동 화면 |
디테일 뷰 | 디테일 뷰 이동 | |
---|---|---|
작동 화면 |
카테고리 화면 | 검색화면 | |
---|---|---|
작동 화면 |
1️⃣ 세그먼트 스크롤 이슈로 인한 커스텀 진행 🔒 문제점
- 이미지 업로드를 위해 세그먼트의 텍스트를 네트워크 요청을 통해 가져오면서 뷰 재사용을 계획했습니다. 하지만 세그먼트 카테고리가 많아 가로 스크롤이 필요한 상황에서, 세그먼트가 스크롤을 지원하지 않는 문제로 인해 진행이 어려웠습니다.
세그먼트 스크롤 |
---|
🔑 해결방법
- 이 문제를 해결하기 위해, 세그먼트와 유사하게 동작하는 버튼을 커스텀 디자인하여 사용하였습니다. 많은 카테고리를 수용할 수 있으며 스크롤이 가능해 사용자 인터페이스를 구현할 수 있었습니다.
private func setupCategoryButtons() {
var previousButton: UIButton?
items.enumerated().forEach { index, category in
let action = UIAction(title: category, handler: { [weak self] _ in
self?.delegate?.categoryDidSelect(at: index)
})
let button = UIButton(type: .system, primaryAction: action)
button.tag = index
button.setTitle(category, for: .normal)
button.tintColor = .white
scrollView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.topAnchor.constraint(equalTo: scrollView.topAnchor),
button.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
button.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
])
if let previousButton = previousButton {
button.leadingAnchor.constraint(equalTo: previousButton.trailingAnchor, constant: 12).isActive = true
} else {
button.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16).isActive = true
}
previousButton = button
buttons.append(button)
}
if let lastButton = previousButton {
NSLayoutConstraint.activate([
lastButton.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -20)
])
}
}
2️⃣ 사진 업로드 속도 🔒 문제점
- 사진의 디테일뷰로 들어가 사진을 확인 할 경우 좌,우 이동시에 사진 업로드가 미리 준비되어있지않아 업로드가 오래 걸리게되었습니다.
제스쳐 진행시 사진 |
---|
- 제스쳐를 사용하여 사진의 인덱스에 맞게 사진을 업로드하게 진행했습니다.
@objc private func handleSwipeToLeft(_ gesture: UISwipeGestureRecognizer) {
let newFrame = photoDetailView.frame.offsetBy(dx: -view.frame.width, dy: 0)
UIView.animate(withDuration: 0.25, animations: { [weak self] in
self?.photoDetailView.frame = newFrame
self?.viewModel.showNextPhoto()
}) { _ in
self.photoDetailView.frame = self.view.bounds
}
}
🔑 해결방법
- 기존의 제스처 기반 사진 업로드 방식에서 컬렉션뷰를 사용하는 방식으로 변경했습니다. 컬렉션뷰를 사용하면 사진들이 미리 준비되고 효율적으로 관리될 수 있어, 사용자 경험이 크게 향상됩니다.
컬렉션뷰 사용 |
---|
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout
extension PhotoDetailViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
viewModel.photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Detailcell", for: indexPath) as? PhotoDetaillViewCell,
let photo = viewModel.photos[safe: indexPath.row] else {
return UICollectionViewCell()
}
cell.configure(photo: photo, isUIElementsHidden: viewModel.isUIElementsHidden)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
}
3️⃣ 이미지 레이아웃 변경 🔒 문제점
- 터치 제스쳐 진행시에 버튼 및 네비게이션바가 사라질때 이미지레이아웃이 변경되는 이슈가 발생했습니다.
뷰데이터 히든 이슈 |
---|
@objc private func handleTap() {
guard let navigationController = navigationController else { return }
let shouldHide = navigationController.navigationBar.isHidden == false
navigationController.setNavigationBarHidden(shouldHide, animated: true)
photoDetailView.toggleUIElements(shouldHide: shouldHide)
}
func toggleUIElements(shouldHide: Bool) {
UIView.animate(withDuration: 0.25) {
self.likeButton.isHidden = shouldHide
self.addButton.isHidden = shouldHide
self.downloadButton.isHidden = shouldHide
}
}
🔑 해결방법
뷰데이터 알파값 처리 |
---|
isHidden
처리가 아닌alpha
값을 조절 하여 on/off기능 생성, 진행 했습니다.
@objc private func handleTap() {
guard let navigationController = navigationController else { return }
let shouldHide = navigationController.navigationBar.alpha == 1
UIView.animate(withDuration: 0.25) {
navigationController.navigationBar.alpha = shouldHide ? 0 : 1
self.photoDetailView.toggleUIElements(shouldHide: shouldHide)
}
}
func toggleUIElements(shouldHide: Bool) {
UIView.animate(withDuration: 0.25) {
self.likeButton.alpha = shouldHide ? 0 : 1
self.addButton.alpha = shouldHide ? 0 : 1
self.downloadButton.alpha = shouldHide ? 0 : 1
}
}
🌟 API
통신을 통하여 Network
구축 진행
UnsplashEndPoint
, URLSessionProvider
, UnsplashRepository
등의 레이어와 프로토콜을 구현하여 네트워크 통신 구조를 설계했습니다. 프로토콜을 활용하여 인터페이스의 추상화를 진행했으며, 다른 요구 사항에 유연하게 대응할 수 있는 설계를 구현했습니다.
🌟 MVVVM
아키텍쳐 구현
MVVM
아키텍처를 적용하여, 뷰모델의 상태 변화에 따른 비동기적 화면 업데이트를 구현했습니다. 프로토콜을 통하여 역활을 추상화 하였고 각 계층의 책임을 명확히 했습니다. 아키텍처의 분리를 통해 보다 유지보수가 용이하게 진행했습니다.
🌟 SwiftUI
를 통하여 뷰 생성
SwiftUI
를 활용하여 SearchView
관련하여 인터페이스 구현 진행하여 선언적UI를 통한 뷰 개발을 이해했습니다.
🌟 CollectionView
를 통하여 뷰 생성
서버로부터 받아온 이미지를 데이터에 맞게 CollectionView
셀의 크기를 동적으로 조정하는 기능을 구현했습니다.
🌟 UserDefaults
를 통하여 좋아요 구현
UserDefaults
를 활용해 애플리케이션 내 좋아요 기능을 구현했습니다. 사용자가 좋아하는 사진을 로컬 저장소에 저장하여 확인할 수 있게 진행했습니다.