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

커스텀 앨범 생성 #27

Merged
merged 8 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = MHApplication/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "기록소";
INFOPLIST_KEY_NSCameraUsageDescription = "기록소는 카메라 권한을 필요로 합니다. 허용 안 함 시 일부 기능이 동작하지 않을 수 있습니다.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "기록소는 사진 권한을 필요로 합니다. 허용 안 함 시 일부 기능이 동작하지 않을 수 있습니다.";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.lifestyle";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
Expand Down Expand Up @@ -311,6 +313,8 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = MHApplication/Resource/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "기록소";
INFOPLIST_KEY_NSCameraUsageDescription = "기록소는 카메라 권한을 필요로 합니다. 허용 안 함 시 일부 기능이 동작하지 않을 수 있습니다.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "기록소는 사진 권한을 필요로 합니다. 허용 안 함 시 일부 기능이 동작하지 않을 수 있습니다.";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.lifestyle";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
0E7F291B2CDB530E007D4F2B /* UILabel+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F291A2CDB5302007D4F2B /* UILabel+Style.swift */; };
0E7F291D2CDB53B5007D4F2B /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7F291C2CDB53B5007D4F2B /* Colors.xcassets */; };
0E7F291F2CDB5D66007D4F2B /* Colors.xcassets in Headers */ = {isa = PBXBuildFile; fileRef = 0E7F291C2CDB53B5007D4F2B /* Colors.xcassets */; settings = {ATTRIBUTES = (Public, ); }; };
A840E5932CDE266F002A1C94 /* UICollectionViewCell+Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = A840E5922CDE266F002A1C94 /* UICollectionViewCell+Identifier.swift */; };
A8A32C532CDBB9980078B4F6 /* CustomAlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A32C522CDBB9980078B4F6 /* CustomAlbumViewController.swift */; };
A8A32C572CDBBB500078B4F6 /* CustomAlbumCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A32C562CDBBB500078B4F6 /* CustomAlbumCollectionViewCell.swift */; };
A8A32C592CDC98D40078B4F6 /* OwnglyphBerry.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A8A32C582CDC98D40078B4F6 /* OwnglyphBerry.ttf */; };
A8A32C5A2CDC9C550078B4F6 /* OwnglyphBerry.ttf in Headers */ = {isa = PBXBuildFile; fileRef = A8A32C582CDC98D40078B4F6 /* OwnglyphBerry.ttf */; settings = {ATTRIBUTES = (Public, ); }; };
CE9AEBAB2CD7B51D00F8471D /* MHDomain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE9AEBAA2CD7B51D00F8471D /* MHDomain.framework */; };
Expand All @@ -23,6 +26,9 @@
0E7F29182CDB52D4007D4F2B /* UIFont+Ownglyph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Ownglyph.swift"; sourceTree = "<group>"; };
0E7F291A2CDB5302007D4F2B /* UILabel+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Style.swift"; sourceTree = "<group>"; };
0E7F291C2CDB53B5007D4F2B /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
A840E5922CDE266F002A1C94 /* UICollectionViewCell+Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+Identifier.swift"; sourceTree = "<group>"; };
A8A32C522CDBB9980078B4F6 /* CustomAlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlbumViewController.swift; sourceTree = "<group>"; };
A8A32C562CDBBB500078B4F6 /* CustomAlbumCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlbumCollectionViewCell.swift; sourceTree = "<group>"; };
A8A32C582CDC98D40078B4F6 /* OwnglyphBerry.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = OwnglyphBerry.ttf; sourceTree = "<group>"; };
CE9AEB992CD7B4F200F8471D /* MHPresentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MHPresentation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE9AEBAA2CD7B51D00F8471D /* MHDomain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MHDomain.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -57,6 +63,7 @@
0E7F28E62CDA6B63007D4F2B /* Source */ = {
isa = PBXGroup;
children = (
A8A32C5C2CDCEBD60078B4F6 /* CustomAlbum */,
0E7F29172CDB52CC007D4F2B /* Extensions */,
);
path = Source;
Expand All @@ -75,10 +82,20 @@
children = (
0E7F291A2CDB5302007D4F2B /* UILabel+Style.swift */,
0E7F29182CDB52D4007D4F2B /* UIFont+Ownglyph.swift */,
A840E5922CDE266F002A1C94 /* UICollectionViewCell+Identifier.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
A8A32C5C2CDCEBD60078B4F6 /* CustomAlbum */ = {
isa = PBXGroup;
children = (
A8A32C522CDBB9980078B4F6 /* CustomAlbumViewController.swift */,
A8A32C562CDBBB500078B4F6 /* CustomAlbumCollectionViewCell.swift */,
);
path = CustomAlbum;
sourceTree = "<group>";
};
CE9AEB8F2CD7B4F200F8471D = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -226,6 +243,9 @@
buildActionMask = 2147483647;
files = (
0E7F29192CDB52DE007D4F2B /* UIFont+Ownglyph.swift in Sources */,
A8A32C572CDBBB500078B4F6 /* CustomAlbumCollectionViewCell.swift in Sources */,
A8A32C532CDBB9980078B4F6 /* CustomAlbumViewController.swift in Sources */,
A840E5932CDE266F002A1C94 /* UICollectionViewCell+Identifier.swift in Sources */,
0E7F291B2CDB530E007D4F2B /* UILabel+Style.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Camera_duotone.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import UIKit

final class CustomAlbumCollectionViewCell: UICollectionViewCell {
// MARK: - Properties
private let photoImageView: UIImageView = {
let imageView = UIImageView(image: nil)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true

return imageView
}()

// MARK: - Initialize
override init(frame: CGRect) {
super.init(frame: frame)

setup()
configureConstraints()
}

required init?(coder: NSCoder) {
super.init(coder: coder)

setup()
configureConstraints()
}

// MARK: - PrepareForReuse
override func prepareForReuse() {
super.prepareForReuse()

photoImageView.image = nil
}

// MARK: - Configure
private func setup() {
contentView.backgroundColor = .lightGray
}

private func configureConstraints() {
contentView.addSubview(photoImageView)
NSLayoutConstraint.activate([
photoImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
photoImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
photoImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
photoImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}

// MARK: - Set Cell Image
func setPhoto(_ photo: UIImage?) {
photoImageView.image = photo
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import UIKit
import Photos

final class CustomAlbumViewController: UIViewController {
// MARK: - Properties
private var imageAsset: PHFetchResult<PHAsset>?
private let imageManager = PHCachingImageManager()
private let imagePicker = UIImagePickerController()

private lazy var albumCollectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
let cellSize = (self.view.bounds.inset(by: self.view.safeAreaInsets).width - 10) / 3
flowLayout.itemSize = CGSize(width: cellSize, height: cellSize)
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 5
flowLayout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false

return collectionView
}()
iceHood marked this conversation as resolved.
Show resolved Hide resolved

// MARK: - ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()

setup()
configureConstraints()
}

// MARK: - Setup & Configure
private func setup() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
Kyxxn marked this conversation as resolved.
Show resolved Hide resolved

imageAsset = PHAsset.fetchAssets(with: fetchOptions)
imagePicker.delegate = self
albumCollectionView.delegate = self
albumCollectionView.dataSource = self
albumCollectionView.register(
CustomAlbumCollectionViewCell.self,
forCellWithReuseIdentifier: CustomAlbumCollectionViewCell.identifier
)
}

private func configureConstraints() {
view.addSubview(albumCollectionView)

NSLayoutConstraint.activate([
albumCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
albumCollectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
albumCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
albumCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}

// MARK: - Open Camera
private func openCamera() {
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {

Check warning on line 59 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Control Statement Violation: `if`, `for`, `guard`, `switch`, `while`, and `catch` statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses (control_statement)

Check warning on line 59 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Control Statement Violation: `if`, `for`, `guard`, `switch`, `while`, and `catch` statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses (control_statement)
imagePicker.sourceType = .camera
navigationController?.pushViewController(imagePicker, animated: true)
} else {
// TODO: - 카메라 접근 권한 Alert

Check warning on line 63 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 카메라 접근 권한 Alert) (todo)

Check warning on line 63 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 카메라 접근 권한 Alert) (todo)
}
}
}

// MARK: - UICollectionViewDelegate
extension CustomAlbumViewController: UICollectionViewDelegate {
func collectionView(
_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath
) {
if indexPath.item == 0 {
self.openCamera()
} else {
guard let asset = self.imageAsset?[indexPath.item - 1] else { return }
imageManager.requestImage(
for: asset,
targetSize: .zero,
contentMode: .default,
options: nil
) { image, _ in

Check warning on line 83 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Unused Closure Parameter Violation: Unused parameter in a closure should be replaced with _ (unused_closure_parameter)

Check warning on line 83 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Unused Closure Parameter Violation: Unused parameter in a closure should be replaced with _ (unused_closure_parameter)
// TODO: - 이미지 편집 뷰 로 이동

Check warning on line 84 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 이미지 편집 뷰 로 이동) (todo)

Check warning on line 84 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 이미지 편집 뷰 로 이동) (todo)
}
}
}
}

// MARK: - UICollectionViewDataSource
extension CustomAlbumViewController: UICollectionViewDataSource {
func collectionView(
_ collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int {
guard let imageAsset else { return 1 }
return imageAsset.count + 1
}

func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: CustomAlbumCollectionViewCell.identifier,
for: indexPath
) as? CustomAlbumCollectionViewCell else { return UICollectionViewCell() }

if indexPath.item == 0 {
cell.setPhoto(.photo)
} else {
guard let asset = imageAsset?[indexPath.item - 1] else { return cell }
let cellSize = cell.bounds.size
imageManager.requestImage(
for: asset,
targetSize: cellSize,
contentMode: .aspectFill,
options: nil
) { image, _ in
cell.setPhoto(image)
}
}

return cell
}
}

// MARK: - UIImagePickerControllerDelegate, UINavigationControllerDelegate
extension CustomAlbumViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]

Check warning on line 132 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals (colon)

Check warning on line 132 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals (colon)
) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
// TODO: - 이미지 편집 뷰로 이동

Check warning on line 135 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 이미지 편집 뷰로 이동) (todo)

Check warning on line 135 in MemorialHouse/MHPresentation/MHPresentation/Source/CustomAlbum/CustomAlbumViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (- 이미지 편집 뷰로 이동) (todo)
}
dismiss(animated: true, completion: nil)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import UIKit

extension UICollectionViewCell {
static var identifier: String {
String(describing: self)
}
}
Loading