diff --git a/SpaceCapsule/SpaceCapsule.xcodeproj/project.pbxproj b/SpaceCapsule/SpaceCapsule.xcodeproj/project.pbxproj index 2e92f46..872d2af 100644 --- a/SpaceCapsule/SpaceCapsule.xcodeproj/project.pbxproj +++ b/SpaceCapsule/SpaceCapsule.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 0D1331C7293842D300B2CD28 /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1331C6293842D300B2CD28 /* Address.swift */; }; + 0D219825294730A600B7401B /* ThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D219824294730A600B7401B /* ThemeButton.swift */; }; + 0D21982729473A2700B7401B /* CapsuleThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D21982629473A2700B7401B /* CapsuleThumbnailView.swift */; }; 0D46DEFA2923E1180045400A /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D46DEF92923E1180045400A /* UIFont+.swift */; }; 0D4C11372927489D00B1C2B6 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4C11362927489D00B1C2B6 /* UIViewPreview.swift */; }; 0D4C11392927495900B1C2B6 /* UIViewControllerPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4C11382927495900B1C2B6 /* UIViewControllerPreview.swift */; }; @@ -45,6 +47,7 @@ 0DB746AE292DF8F6005FF249 /* Capsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB746AD292DF8F6005FF249 /* Capsule.swift */; }; 0DB746B4292E268A005FF249 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB746B3292E268A005FF249 /* Date+.swift */; }; 0DB88C712935E83600107DC3 /* AppDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB88C702935E83600107DC3 /* AppDataManager.swift */; }; + 0DC27FA0294B5F240019FD71 /* LockImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC27F9F294B5F240019FD71 /* LockImageView.swift */; }; 0DBD20A629519E3500C74F4F /* KingReceiver in Frameworks */ = {isa = PBXBuildFile; productRef = 0DBD20A529519E3500C74F4F /* KingReceiver */; }; 0DCB22932924CE58002CB095 /* CustomTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DCB22922924CE58002CB095 /* CustomTabBarController.swift */; }; 0DCEED2D29320B28006EB2B4 /* UILabel+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DCEED2C29320B28006EB2B4 /* UILabel+.swift */; }; @@ -172,7 +175,7 @@ 59D8CFB52935FDAC00FD9CF6 /* CarouselCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8CFB42935FDAC00FD9CF6 /* CarouselCollectionViewFlowLayout.swift */; }; 59D8CFB8293609D100FD9CF6 /* HomeCapsuleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8CFB7293609D100FD9CF6 /* HomeCapsuleCell.swift */; }; 59D8CFBA293609E000FD9CF6 /* HomeCapsuleCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8CFB9293609E000FD9CF6 /* HomeCapsuleCellItem.swift */; }; - 59D8CFBC29360A8900FD9CF6 /* ThemeThumbnailImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8CFBB29360A8900FD9CF6 /* ThemeThumbnailImageView.swift */; }; + 59D8CFBC29360A8900FD9CF6 /* ThumbnailImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8CFBB29360A8900FD9CF6 /* ThumbnailImageView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -194,6 +197,8 @@ /* Begin PBXFileReference section */ 0D1331C6293842D300B2CD28 /* Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Address.swift; sourceTree = ""; }; + 0D219824294730A600B7401B /* ThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeButton.swift; sourceTree = ""; }; + 0D21982629473A2700B7401B /* CapsuleThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleThumbnailView.swift; sourceTree = ""; }; 0D2B7AB92934594400CE34E3 /* running.gpx */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = running.gpx; sourceTree = ""; }; 0D46DEF92923E1180045400A /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; 0D4C11362927489D00B1C2B6 /* UIViewPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = ""; }; @@ -233,6 +238,7 @@ 0DB746AD292DF8F6005FF249 /* Capsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Capsule.swift; sourceTree = ""; }; 0DB746B3292E268A005FF249 /* Date+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+.swift"; sourceTree = ""; }; 0DB88C702935E83600107DC3 /* AppDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDataManager.swift; sourceTree = ""; }; + 0DC27F9F294B5F240019FD71 /* LockImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockImageView.swift; sourceTree = ""; }; 0DCB22922924CE58002CB095 /* CustomTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTabBarController.swift; sourceTree = ""; }; 0DCEED2C29320B28006EB2B4 /* UILabel+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+.swift"; sourceTree = ""; }; 0DCEED2E29320EAB006EB2B4 /* CustomCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCodable.swift; sourceTree = ""; }; @@ -353,7 +359,7 @@ 59D8CFB42935FDAC00FD9CF6 /* CarouselCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarouselCollectionViewFlowLayout.swift; sourceTree = ""; }; 59D8CFB7293609D100FD9CF6 /* HomeCapsuleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCapsuleCell.swift; sourceTree = ""; }; 59D8CFB9293609E000FD9CF6 /* HomeCapsuleCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCapsuleCellItem.swift; sourceTree = ""; }; - 59D8CFBB29360A8900FD9CF6 /* ThemeThumbnailImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeThumbnailImageView.swift; sourceTree = ""; }; + 59D8CFBB29360A8900FD9CF6 /* ThumbnailImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailImageView.swift; sourceTree = ""; }; 59F567F72923BE1E00F756B2 /* SpaceCapsule.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SpaceCapsule.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ @@ -395,13 +401,14 @@ 0D46DEFC2923E4800045400A /* Components */ = { isa = PBXGroup; children = ( + 4A1D1BC52941F3C500F47ACB /* BlurEffectView.swift */, + 0D21982629473A2700B7401B /* CapsuleThumbnailView.swift */, 0DA05E1C29308B8400B08FA5 /* CustomNavigationController.swift */, 0DD83D0E292F1BAD0020F15C /* CustomScrollView.swift */, 0DCB22922924CE58002CB095 /* CustomTabBarController.swift */, - 0DFF50DE2940587600079FA6 /* RefreshButton.swift */, 0DFF50DC29402E3100079FA6 /* EmptyView.swift */, 0DD83D12292F51960020F15C /* LoadingIndicatorView.swift */, - 4A1D1BC52941F3C500F47ACB /* BlurEffectView.swift */, + 0DFF50DE2940587600079FA6 /* RefreshButton.swift */, 0D9A834C293788A300D00303 /* Map */, 0DCB22942924CE61002CB095 /* Theme */, ); @@ -556,11 +563,12 @@ 0DCB22942924CE61002CB095 /* Theme */ = { isa = PBXGroup; children = ( - 59D8CFBB29360A8900FD9CF6 /* ThemeThumbnailImageView.swift */, + 2885A9FF293B6466003E5311 /* ProfileButton.swift */, + 0D77D7C8292B5D490038D054 /* SelectButton.swift */, + 0D219824294730A600B7401B /* ThemeButton.swift */, 0D9D672D292486780038F529 /* ThemeLabel.swift */, 0DA6610C292778BE00CF3563 /* ThemeTextField.swift */, - 0D77D7C8292B5D490038D054 /* SelectButton.swift */, - 2885A9FF293B6466003E5311 /* ProfileButton.swift */, + 59D8CFBB29360A8900FD9CF6 /* ThumbnailImageView.swift */, ); path = Theme; sourceTree = ""; @@ -594,6 +602,7 @@ 2807BC0A292C658000288A5D /* ListCapsuleCell.swift */, 2800F979292F1F060069EE7F /* ListCapsuleCellItem.swift */, 28245561293B17B100DE03A1 /* UnOpenable.swift */, + 0DC27F9F294B5F240019FD71 /* LockImageView.swift */, 28E68BDF294AB58C009D608E /* SortButton.swift */, ); path = Components; @@ -642,11 +651,11 @@ children = ( 288A8E912923244D000229D2 /* Coordinator.swift */, 0DCEED2E29320EAB006EB2B4 /* CustomCodable.swift */, + 4AA4C5B429473D8600171D0D /* MovableToCapsuleAccess.swift */, + 0DD5C7C42938453300B23B69 /* Refreshable.swift */, 28049CEF292382BB002389BD /* View */, 28049CF0292382C7002389BD /* ViewController */, 28049CEE2923825A002389BD /* ViewModel */, - 0DD5C7C42938453300B23B69 /* Refreshable.swift */, - 4AA4C5B429473D8600171D0D /* MovableToCapsuleAccess.swift */, ); path = Base; sourceTree = ""; @@ -1155,6 +1164,8 @@ 0DF9D2A5293F1CB3001C05DE /* DetailImageView.swift in Sources */, 0DE786E1292BBDEC007EF260 /* DatePickerViewController.swift in Sources */, 4A1D1BC12940292A00F47ACB /* CapsuleSettingsViewModel.swift in Sources */, + 599C1A4C292CC99A00517E93 /* ImageCacheFactory.swift in Sources */, + 0DC27FA0294B5F240019FD71 /* LockImageView.swift in Sources */, 288A8E9B29235A6E000229D2 /* SignInView.swift in Sources */, 288A8ED529235C6F000229D2 /* CapsuleCreateViewController.swift in Sources */, 4AA4C5B72948729200171D0D /* Calendar+.swift in Sources */, @@ -1203,6 +1214,8 @@ 0DFF50DD29402E3100079FA6 /* EmptyView.swift in Sources */, 28A700D52938AE100049FAF8 /* AnimationResource.swift in Sources */, 288A8EE229235E3F000229D2 /* CapsuleLocateViewModel.swift in Sources */, + 59D8CFBC29360A8900FD9CF6 /* ThumbnailImageView.swift in Sources */, + 0D752DB32944A964004A704C /* UIImage+resize.swift in Sources */, 59D8CFBC29360A8900FD9CF6 /* ThemeThumbnailImageView.swift in Sources */, 0D4C11372927489D00B1C2B6 /* UIViewPreview.swift in Sources */, 288A8EB229235B5A000229D2 /* ProfileView.swift in Sources */, @@ -1223,6 +1236,8 @@ 0DFF50E3294067E600079FA6 /* UIControl+Rx.swift in Sources */, 288A8EA729235AF1000229D2 /* NicknameViewModel.swift in Sources */, 28245562293B17B100DE03A1 /* UnOpenable.swift in Sources */, + 0D21982729473A2700B7401B /* CapsuleThumbnailView.swift in Sources */, + 0D219825294730A600B7401B /* ThemeButton.swift in Sources */, 4A1D1BC3294033C200F47ACB /* CapsuleSettingsView.swift in Sources */, 288A8ECC29235BE9000229D2 /* HomeViewController.swift in Sources */, 0DA6610D292778BE00CF3563 /* ThemeTextField.swift in Sources */, diff --git a/SpaceCapsule/SpaceCapsule.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SpaceCapsule/SpaceCapsule.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index b74200a..0000000 --- a/SpaceCapsule/SpaceCapsule.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,203 +0,0 @@ -{ - "pins" : [ - { - "identity" : "abseil-cpp-swiftpm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git", - "state" : { - "revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1", - "version" : "0.20220203.2" - } - }, - { - "identity" : "bluecryptor", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/BlueCryptor.git", - "state" : { - "revision" : "cec97c24b111351e70e448972a7d3fe68a756d6d", - "version" : "2.0.2" - } - }, - { - "identity" : "blueecc", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/BlueECC.git", - "state" : { - "revision" : "1485268a54f8135435a825a855e733f026fa6cc8", - "version" : "1.2.201" - } - }, - { - "identity" : "bluersa", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/BlueRSA.git", - "state" : { - "revision" : "440f78db26d8bb073f29590f1c7bd31004da09ae", - "version" : "1.0.201" - } - }, - { - "identity" : "boringssl-swiftpm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/boringssl-SwiftPM.git", - "state" : { - "revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab", - "version" : "0.9.1" - } - }, - { - "identity" : "firebase-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/firebase-ios-sdk", - "state" : { - "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050", - "version" : "9.6.0" - } - }, - { - "identity" : "googleappmeasurement", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleAppMeasurement.git", - "state" : { - "revision" : "c1cfde8067668027b23a42c29d11c246152fe046", - "version" : "9.6.0" - } - }, - { - "identity" : "googledatatransport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleDataTransport.git", - "state" : { - "revision" : "5056b15c5acbb90cd214fe4d6138bdf5a740e5a8", - "version" : "9.2.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "6db6edb48bdd9943426562c7f042a5492de5ba3d", - "version" : "7.10.0" - } - }, - { - "identity" : "grpc-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/grpc/grpc-ios.git", - "state" : { - "revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6", - "version" : "1.44.3-grpc" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "5ccda3981422a84186387dbb763ba739178b529c", - "version" : "2.3.0" - } - }, - { - "identity" : "kingreceiver", - "kind" : "remoteSourceControl", - "location" : "https://github.com/trumanfromkorea/KingReceiver.git", - "state" : { - "branch" : "main", - "revision" : "fc840dec1f236230177e639e056b5dd8e166fa2b" - } - }, - { - "identity" : "kituracontracts", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/KituraContracts.git", - "state" : { - "revision" : "6edf7ac3dd2b3a2c61284778d430bbad7d8a6f23", - "version" : "2.0.1" - } - }, - { - "identity" : "leveldb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/leveldb.git", - "state" : { - "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", - "version" : "1.22.2" - } - }, - { - "identity" : "loggerapi", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/LoggerAPI.git", - "state" : { - "revision" : "4e6b45e850ffa275e8e26a24c6454fd709d5b6ac", - "version" : "2.0.0" - } - }, - { - "identity" : "nanopb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/nanopb.git", - "state" : { - "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", - "version" : "2.30909.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb", - "version" : "2.1.1" - } - }, - { - "identity" : "rxswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ReactiveX/RxSwift", - "state" : { - "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version" : "6.5.0" - } - }, - { - "identity" : "snapkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SnapKit/SnapKit.git", - "state" : { - "revision" : "f222cbdf325885926566172f6f5f06af95473158", - "version" : "5.6.0" - } - }, - { - "identity" : "swift-jwt", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Kitura/Swift-JWT.git", - "state" : { - "revision" : "08e02ff214c41df49bdd189ff837d68ba11c437b", - "version" : "4.0.0" - } - }, - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c", - "version" : "1.4.4" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "ab3a58b7209a17d781c0d1dbb3e1ff3da306bae8", - "version" : "1.20.3" - } - } - ], - "version" : 2 -} diff --git a/SpaceCapsule/SpaceCapsule/Base/ViewModel/CapsuleCellItemNeedable.swift b/SpaceCapsule/SpaceCapsule/Base/ViewModel/CapsuleCellItemNeedable.swift index 258cc4c..064aec3 100644 --- a/SpaceCapsule/SpaceCapsule/Base/ViewModel/CapsuleCellItemNeedable.swift +++ b/SpaceCapsule/SpaceCapsule/Base/ViewModel/CapsuleCellItemNeedable.swift @@ -17,7 +17,7 @@ extension CapsuleCellNeedable { let capsuleCellItem = ListCapsuleCellItem( uuid: capsule.uuid, - thumbnailImageURL: capsule.images.first, + thumbnailImageURL: capsule.images.first ?? "", address: capsule.simpleAddress, closedDate: capsule.closedDate, memoryDate: capsule.memoryDate, diff --git a/SpaceCapsule/SpaceCapsule/Components/BlurEffectView.swift b/SpaceCapsule/SpaceCapsule/Components/BlurEffectView.swift index 79a58db..a6bf992 100644 --- a/SpaceCapsule/SpaceCapsule/Components/BlurEffectView.swift +++ b/SpaceCapsule/SpaceCapsule/Components/BlurEffectView.swift @@ -8,9 +8,9 @@ import UIKit final class CapsuleBlurEffectView: UIVisualEffectView { - init() { + init(width: CGFloat) { super.init(effect: UIBlurEffect(style: .systemUltraThinMaterialDark)) - self.layer.cornerRadius = FrameResource.listCapsuleCellWidth / 2 + self.layer.cornerRadius = width / 2 self.clipsToBounds = true } diff --git a/SpaceCapsule/SpaceCapsule/Components/CapsuleThumbnailView.swift b/SpaceCapsule/SpaceCapsule/Components/CapsuleThumbnailView.swift new file mode 100644 index 0000000..fa5698b --- /dev/null +++ b/SpaceCapsule/SpaceCapsule/Components/CapsuleThumbnailView.swift @@ -0,0 +1,113 @@ +// +// CapsuleOpenCloseView.swift +// SpaceCapsule +// +// Created by 장재훈 on 2022/12/12. +// + +import AVFoundation +import SnapKit +import UIKit + +class CapsuleThumbnailView: UIView { + struct Item { + let thumbnailImageURL: String + let closedDateString: String + let memoryDateString: String + let simpleAddress: String + } + + let thumbnailImageView = ThumbnailImageView( + frame: .zero, + width: UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio + ) + + let descriptionLabel = { + let label = ThemeLabel(size: FrameResource.fontSize140, color: .themeGray300) + label.numberOfLines = 3 + label.textAlignment = .center + + return label + }() + + let bottomButton = ThemeButton(title: " ") + + // MARK: - Lifecycle + + override init(frame: CGRect) { + super.init(frame: frame) + + configure() + addSubViews() + makeConstraints() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Methods + + func configure() { + backgroundColor = .themeBackground + } + + func configure(item: Item) { + descriptionLabel.asFontColor( + targetStringList: [item.memoryDateString, item.simpleAddress], + size: FrameResource.fontSize140, + color: .themeGray400 + ) + + thumbnailImageView.imageView.kr.setImage( + with: item.thumbnailImageURL, + placeholder: .empty, + scale: FrameResource.closedImageScale + ) + } + + func addSubViews() { + [thumbnailImageView, descriptionLabel, bottomButton].forEach { + addSubview($0) + } + } + + func makeConstraints() { + thumbnailImageView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalToSuperview().multipliedBy(AnimationResource.fromOriginY) + $0.width.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) + $0.height.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio * FrameResource.capsuleThumbnailHWRatio) + } + + descriptionLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalTo(self.snp.centerY).multipliedBy(0.7) + .offset(FrameResource.capsuleThumbnailHeight / 2 + AnimationResource.capsuleMoveHeight) + $0.bottom.equalTo(bottomButton.snp.top).offset(-FrameResource.spacing200).priority(999) + } + + bottomButton.snp.makeConstraints { + $0.leading.equalToSuperview().offset(FrameResource.horizontalPadding) + $0.trailing.equalToSuperview().offset(-FrameResource.horizontalPadding) + $0.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).offset(-FrameResource.spacing200) + $0.height.equalTo(FrameResource.buttonHeight) + } + } + + func animate() { + UIView.animate(withDuration: AnimationResource.capsuleMoveDuration, animations: { + self.layoutIfNeeded() + self.thumbnailImageView.center.y = (self.frame.height * AnimationResource.destinationHeightRatio) + }, completion: { _ in + UIView.animate( + withDuration: AnimationResource.capsuleMoveDuration, + delay: 0, + options: [.repeat, .autoreverse] + ) { + self.thumbnailImageView.transform = .init(translationX: 0, y: AnimationResource.capsuleMoveHeight) + } + }) + } +} diff --git a/SpaceCapsule/SpaceCapsule/Components/Theme/ThemeButton.swift b/SpaceCapsule/SpaceCapsule/Components/Theme/ThemeButton.swift new file mode 100644 index 0000000..3b7b369 --- /dev/null +++ b/SpaceCapsule/SpaceCapsule/Components/Theme/ThemeButton.swift @@ -0,0 +1,26 @@ +// +// ThemeButton.swift +// SpaceCapsule +// +// Created by 장재훈 on 2022/12/12. +// + +import UIKit + +final class ThemeButton: UIButton { + override var isEnabled: Bool { + didSet { + backgroundColor = isEnabled ? .themeColor200 : .themeGray200 + } + } + + convenience init(title: String = " ") { + self.init() + + setTitle(title, for: .normal) + setTitleColor(.white, for: .normal) + titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) + backgroundColor = .themeColor200 + layer.cornerRadius = FrameResource.commonCornerRadius + } +} diff --git a/SpaceCapsule/SpaceCapsule/Components/Theme/ThemeThumbnailImageView.swift b/SpaceCapsule/SpaceCapsule/Components/Theme/ThumbnailImageView.swift similarity index 88% rename from SpaceCapsule/SpaceCapsule/Components/Theme/ThemeThumbnailImageView.swift rename to SpaceCapsule/SpaceCapsule/Components/Theme/ThumbnailImageView.swift index 0cc6509..b1d1660 100644 --- a/SpaceCapsule/SpaceCapsule/Components/Theme/ThemeThumbnailImageView.swift +++ b/SpaceCapsule/SpaceCapsule/Components/Theme/ThumbnailImageView.swift @@ -7,7 +7,7 @@ import UIKit -final class ThemeThumbnailImageView: UIView { +final class ThumbnailImageView: UIView { // MARK: - Properties let width: CGFloat @@ -20,6 +20,7 @@ final class ThemeThumbnailImageView: UIView { imageView.clipsToBounds = true imageView.backgroundColor = .white imageView.contentMode = .scaleAspectFill + return imageView }() @@ -27,6 +28,7 @@ final class ThemeThumbnailImageView: UIView { init(frame: CGRect, width: CGFloat) { self.width = width + print(width) super.init(frame: frame) configure() addSubViews() @@ -40,7 +42,7 @@ final class ThemeThumbnailImageView: UIView { // MARK: - Methods - func configure() { + private func configure() { backgroundColor = .themeGray100 layer.shadowOffset = FrameResource.capsuleCellShadowOffset layer.shadowRadius = FrameResource.capsuleCellShadowRadius @@ -48,13 +50,13 @@ final class ThemeThumbnailImageView: UIView { layer.cornerRadius = width / 2 } - func addSubViews() { + private func addSubViews() { [imageView].forEach { addSubview($0) } } - func makeConstraints() { + private func makeConstraints() { imageView.snp.makeConstraints { $0.edges.equalToSuperview() } diff --git a/SpaceCapsule/SpaceCapsule/Manager/LocationManager.swift b/SpaceCapsule/SpaceCapsule/Manager/LocationManager.swift index 43b858f..81a0a0c 100644 --- a/SpaceCapsule/SpaceCapsule/Manager/LocationManager.swift +++ b/SpaceCapsule/SpaceCapsule/Manager/LocationManager.swift @@ -93,8 +93,10 @@ final class LocationManager { guard let currentCoordinate = coordinate else { return 0.0 } + let currentLocation = location(currentCoordinate) let capsuleLocation = location(capsuleCoordinate) + return currentLocation.distance(from: capsuleLocation) } diff --git a/SpaceCapsule/SpaceCapsule/Scene/Auth/Nickname/NicknameView.swift b/SpaceCapsule/SpaceCapsule/Scene/Auth/Nickname/NicknameView.swift index 523bb6e..82a4b71 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/Auth/Nickname/NicknameView.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/Auth/Nickname/NicknameView.swift @@ -16,15 +16,7 @@ final class NicknameView: UIView, BaseView { let nicknameTextField = ThemeTextField(placeholder: "닉네임을 입력해주세요") - let doneButton: UIButton = { - let button = UIButton() - button.setTitle("완료", for: .normal) - button.titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) - button.backgroundColor = .themeColor200 - button.layer.cornerRadius = FrameResource.commonCornerRadius - - return button - }() + let doneButton = ThemeButton(title: "완료") // MARK: - Lifecycles diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenView.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenView.swift index d98cf50..5a47b87 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenView.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenView.swift @@ -10,47 +10,23 @@ import KingReceiver import SnapKit import UIKit -final class CapsuleOpenView: UIView, BaseView, UnOpenable { - var thumbnailImageView = ThemeThumbnailImageView(frame: .zero, width: UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) - - var descriptionLabel = { - let label = ThemeLabel(text: nil, size: FrameResource.fontSize140, color: .themeGray300) - label.numberOfLines = 3 - label.textAlignment = .center - return label - }() - - let blurEffectView = CapsuleBlurEffectView() +final class CapsuleOpenView: CapsuleThumbnailView, BaseView, UnOpenable { + let blurEffectView = CapsuleBlurEffectView(width: UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) - var lockImageView = { - let lockImageView = UIImageView() - lockImageView.image = .lock - lockImageView.tintColor = .themeGray200 - return lockImageView - }() + var lockImageView = LockImageView() - var dateLabel = { - let dateLabel = ThemeLabel(text: nil, size: FrameResource.fontSize80, color: .themeGray200) + var closedDateLabel = { + let dateLabel = ThemeLabel(size: FrameResource.fontSize80, color: .themeGray200) dateLabel.textAlignment = .center - return dateLabel - }() - - var openButton = { - let button = UIButton() - button.titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) - button.setTitle("열기", for: .normal) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .themeColor200 - button.layer.cornerRadius = FrameResource.commonCornerRadius + dateLabel.numberOfLines = 0 - return button + return dateLabel }() // MARK: - Lifecycle override init(frame: CGRect) { super.init(frame: frame) - configure() addSubViews() makeConstraints() @@ -63,89 +39,39 @@ final class CapsuleOpenView: UIView, BaseView, UnOpenable { // MARK: - Methods - func configure() { - backgroundColor = .themeBackground - } - - func configure(capsuleCellItem: ListCapsuleCellItem) { - if let thumbnailURL = capsuleCellItem.thumbnailImageURL { - thumbnailImageView.imageView.kr.setImage(with: thumbnailURL, placeholder: .empty, scale: FrameResource.openableImageScale) - } + func configure(item: Item, isOpenable: Bool) { + bottomButton.setTitle("열기", for: .normal) descriptionLabel.text = """ - \(capsuleCellItem.memoryDate.dateString) - \(capsuleCellItem.address) 에서의 - 추억을 담은 캡슐 + \(item.memoryDateString) + \(item.simpleAddress) 에서의 + 추억이 담긴 캡슐을 보관하였습니다. """ - descriptionLabel.asFontColor( - targetStringList: [capsuleCellItem.memoryDate.dateString, capsuleCellItem.address], - size: FrameResource.fontSize140, - color: .themeGray400 - ) - dateLabel.text = "밀봉시간: \(capsuleCellItem.closedDate.dateTimeString)" - if !capsuleCellItem.isOpenable() { - openButton.backgroundColor = .themeGray200 -// openButton.isEnabled = false - applyUnOpenableEffect() - } - } - - func addSubViews() { - [thumbnailImageView, descriptionLabel, openButton].forEach { - addSubview($0) - } - } - - func makeConstraints() { - thumbnailImageView.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.centerY.equalToSuperview().multipliedBy(AnimationResource.fromOriginY) - $0.width.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) - $0.height.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio * FrameResource.capsuleThumbnailHWRatio) - } + closedDateLabel.text = "밀봉시간 \(item.closedDateString)" - descriptionLabel.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.top.equalTo(self.snp.centerY).multipliedBy(0.7) - .offset(FrameResource.capsuleThumbnailHeight / 2 + AnimationResource.capsuleMoveHeight) - $0.bottom.equalTo(openButton.snp.top).offset(-FrameResource.spacing200).priority(999) + if !isOpenable { + applyUnopenableEffect(superview: thumbnailImageView) } - openButton.snp.makeConstraints { - $0.leading.equalToSuperview().offset(FrameResource.horizontalPadding) - $0.trailing.equalToSuperview().offset(-FrameResource.horizontalPadding) - $0.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).offset(-FrameResource.spacing200) - $0.height.equalTo(FrameResource.buttonHeight) - } - } - - func animate() { - UIView.animate(withDuration: AnimationResource.capsuleMoveDuration, animations: { - self.layoutIfNeeded() - self.thumbnailImageView.center.y = (self.frame.height * AnimationResource.destinationHeightRatio) - }, completion: { _ in - UIView.animate(withDuration: AnimationResource.capsuleMoveDuration, - delay: 0, - options: [.repeat, .autoreverse] - ) { - self.thumbnailImageView.transform = .init(translationX: 0, y: AnimationResource.capsuleMoveHeight) - } - }) + super.configure(item: item) } func shakeAnimate() { AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) let keyPath = "position" + let animation = CABasicAnimation(keyPath: keyPath) animation.duration = AnimationResource.capsuleShakeDuration animation.repeatCount = AnimationResource.capsuleShakeRepeat animation.autoreverses = true + animation.fromValue = CGPoint( x: thumbnailImageView.center.x - AnimationResource.capsuleShakeWidth, y: thumbnailImageView.center.y ) + animation.toValue = CGPoint( x: thumbnailImageView.center.x + AnimationResource.capsuleShakeWidth, y: thumbnailImageView.center.y diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenViewController.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenViewController.swift index eef542e..2381e55 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenViewController.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAccess/CapsuleOpen/CapsuleOpenViewController.swift @@ -17,9 +17,16 @@ final class CapsuleOpenViewController: UIViewController, BaseViewController { override func viewDidLoad() { super.viewDidLoad() view = capsuleOpenView - - if let capsuleCellItem = viewModel?.capsuleCellItem { - capsuleOpenView.configure(capsuleCellItem: capsuleCellItem) + if let item = viewModel?.capsuleCellItem { + capsuleOpenView.configure( + item: CapsuleThumbnailView.Item( + thumbnailImageURL: item.thumbnailImageURL ?? "", + closedDateString: item.closedDate.dateTimeString, + memoryDateString: item.memoryDate.dateString, + simpleAddress: item.address + ), + isOpenable: item.isOpenable + ) } bind() @@ -41,7 +48,7 @@ final class CapsuleOpenViewController: UIViewController, BaseViewController { } func bind() { - capsuleOpenView.openButton.rx.tap + capsuleOpenView.bottomButton.rx.tap .withUnretained(self) .bind { owner, _ in owner.handleOpenButtonTap() @@ -53,8 +60,8 @@ final class CapsuleOpenViewController: UIViewController, BaseViewController { guard let capsuleCellItem = viewModel?.capsuleCellItem else { return } - - if capsuleCellItem.isOpenable() { + if capsuleCellItem.isOpenable { + capsuleOpenView.shakeAnimate() viewModel?.input.tapOpen.onNext(()) } else { capsuleOpenView.shakeAnimate() diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseView.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseView.swift index cce6eaa..f3af792 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseView.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseView.swift @@ -6,50 +6,20 @@ // import KingReceiver -import AVFoundation import SnapKit import UIKit -final class CapsuleCloseView: UIView, BaseView, UnOpenable { - struct Item { - let closedDateString: String - let memoryDateString: String - let simpleAddress: String - let thumbnailImageURL: String - } - - var thumbnailImageView = ThemeThumbnailImageView(frame: .zero, width: UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) +final class CapsuleCloseView: CapsuleThumbnailView, BaseView, UnOpenable { + let blurEffectView = CapsuleBlurEffectView(width: UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) - let blurEffectView = CapsuleBlurEffectView() + var lockImageView = LockImageView() - var lockImageView = { - let lockImageView = UIImageView() - lockImageView.image = .lock - lockImageView.tintColor = .themeGray200 - return lockImageView - }() - - var dateLabel = { - let dateLabel = ThemeLabel(text: nil, size: FrameResource.fontSize80, color: .themeGray200) + var closedDateLabel = { + let dateLabel = ThemeLabel(size: FrameResource.fontSize80, color: .themeGray200) dateLabel.textAlignment = .center - return dateLabel - }() - - var descriptionLabel = { - let label = ThemeLabel(size: FrameResource.fontSize140, color: .themeGray300) - label.numberOfLines = 3 - label.textAlignment = .center - return label - }() + dateLabel.numberOfLines = 0 - let closeButton = { - let button = UIButton() - button.titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) - button.setTitle("완료", for: .normal) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .themeColor200 - button.layer.cornerRadius = FrameResource.commonCornerRadius - return button + return dateLabel }() // MARK: - Lifecycle @@ -68,12 +38,8 @@ final class CapsuleCloseView: UIView, BaseView, UnOpenable { // MARK: - Methods - func configure() { - backgroundColor = .themeBackground - } - - func configure(item: Item) { - dateLabel.text = "밀봉시간: \(item.closedDateString)" + override func configure(item: Item) { + bottomButton.setTitle("완료", for: .normal) descriptionLabel.text = """ \(item.memoryDateString) @@ -81,52 +47,10 @@ final class CapsuleCloseView: UIView, BaseView, UnOpenable { 추억이 담긴 캡슐을 보관하였습니다. """ - descriptionLabel.asFontColor( - targetStringList: [item.memoryDateString, item.simpleAddress], - size: FrameResource.fontSize140, - color: .themeGray400 - ) - - thumbnailImageView.imageView.kr.setImage(with: item.thumbnailImageURL, placeholder: .empty, scale: FrameResource.closedImageScale) + closedDateLabel.text = "밀봉시간 \(item.closedDateString)" - applyUnOpenableEffect() - } - - func addSubViews() { - [thumbnailImageView, descriptionLabel, closeButton].forEach { - addSubview($0) - } - } - - func makeConstraints() { - thumbnailImageView.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.centerY.equalToSuperview().multipliedBy(0.7) - $0.width.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio) - $0.height.equalTo(UIScreen.main.bounds.width * FrameResource.capsuleThumbnailWidthRatio * FrameResource.capsuleThumbnailHWRatio) - } - - descriptionLabel.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.top.equalTo(self.snp.centerY).multipliedBy(0.7) - .offset(FrameResource.capsuleThumbnailHeight / 2 + AnimationResource.capsuleMoveHeight) - $0.bottom.equalTo(closeButton.snp.top).offset(-FrameResource.spacing200).priority(999) - } - - closeButton.snp.makeConstraints { - $0.leading.equalToSuperview().offset(FrameResource.horizontalPadding) - $0.trailing.equalToSuperview().offset(-FrameResource.horizontalPadding) - $0.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).offset(-FrameResource.spacing200) - $0.height.equalTo(FrameResource.buttonHeight) - } - } + applyUnopenableEffect(superview: thumbnailImageView) - func animate() { - UIView.animate(withDuration: AnimationResource.capsuleMoveDuration, - delay: 0, - options: [.repeat, .autoreverse] - ) { - self.thumbnailImageView.transform = .init(translationX: 0, y: AnimationResource.capsuleMoveHeight) - } + super.configure(item: item) } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseViewController.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseViewController.swift index cbab39b..d4ed364 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseViewController.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleClose/CapsuleCloseViewController.swift @@ -30,7 +30,7 @@ final class CapsuleCloseViewController: UIViewController, BaseViewController { } func bind() { - mainView.closeButton.rx.tap + mainView.bottomButton.rx.tap .withUnretained(self) .bind(onNext: { owner, _ in owner.viewModel?.input.tapClose.onNext(()) @@ -43,10 +43,10 @@ final class CapsuleCloseViewController: UIViewController, BaseViewController { .subscribe(onNext: { owner, capsule in owner.mainView.configure(item: CapsuleCloseView.Item( + thumbnailImageURL: capsule.images[safe: 0] ?? "", closedDateString: capsule.closedDate.dateTimeString, memoryDateString: capsule.memoryDate.dateString, - simpleAddress: capsule.simpleAddress, - thumbnailImageURL: capsule.images[safe: 0] ?? "" + simpleAddress: capsule.simpleAddress ) ) }) diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateView.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateView.swift index 38ee33f..384de70 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateView.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateView.swift @@ -53,16 +53,7 @@ final class CapsuleLocateView: UIView, BaseView { let locationLabel = ThemeLabel(size: FrameResource.fontSize110, color: .themeGray400) - let doneButton: UIButton = { - let button = UIButton() - button.setTitle("완료", for: .normal) - button.titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .themeColor200 - button.layer.cornerRadius = FrameResource.commonCornerRadius - - return button - }() + let doneButton = ThemeButton(title: "완료") // MARK: - Lifecycle diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateViewController.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateViewController.swift index 93a8af3..5209fb0 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateViewController.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/CapsuleLocate/CapsuleLocateViewController.swift @@ -50,7 +50,6 @@ final class CapsuleLocateViewController: UIViewController, BaseViewController { .withUnretained(self) .subscribe(onNext: { owner, state in owner.mainView.doneButton.isEnabled = state - owner.mainView.doneButton.backgroundColor = state ? .themeColor200 : .themeGray200 }) .disposed(by: disposeBag) diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/DatePicker/DatePickerView.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/DatePicker/DatePickerView.swift index 618cc1a..58014b9 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/DatePicker/DatePickerView.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleAdd/CapsuleCreate/DatePicker/DatePickerView.swift @@ -32,18 +32,9 @@ final class DatePickerView: UIView { return button }() - let doneButton: UIButton = { - let button = UIButton() - button.setTitle("완료", for: .normal) - button.titleLabel?.font = .themeFont(ofSize: FrameResource.fontSize100) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .themeColor200 - button.layer.cornerRadius = FrameResource.commonCornerRadius - - return button - }() - - let titleLabel = ThemeLabel(text: "추억 날짜 선택", size: FrameResource.fontSize120, color: .themeBlack) + let doneButton = ThemeButton(title: "완료") + + private let titleLabel = ThemeLabel(text: "추억 날짜 선택", size: FrameResource.fontSize120, color: .themeBlack) override init(frame: CGRect) { super.init(frame: frame) diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/CapsuleListViewModel.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/CapsuleListViewModel.swift index c8eca1c..0f5916e 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/CapsuleListViewModel.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/CapsuleListViewModel.swift @@ -47,7 +47,7 @@ final class CapsuleListViewModel: BaseViewModel, CapsuleCellNeedable { owner.coordinator?.moveToCapsuleAccess(with: capsuleCell) }) } - + func refreshCapsule() { AppDataManager.shared.fetchCapsules() } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCell.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCell.swift index 6baba2f..39eecff 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCell.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCell.swift @@ -10,7 +10,7 @@ import SnapKit import UIKit final class ListCapsuleCell: UICollectionViewCell, UnOpenable { - lazy var thumbnailImageView = ThemeThumbnailImageView(frame: .zero, width: FrameResource.listCapsuleCellWidth) + lazy var thumbnailImageView = ThumbnailImageView(frame: .zero, width: FrameResource.listCapsuleCellWidth) lazy var descriptionLabel = { let label = ThemeLabel(size: FrameResource.fontSize80, color: .themeBlack) @@ -19,17 +19,11 @@ final class ListCapsuleCell: UICollectionViewCell, UnOpenable { return label }() - var blurEffectView = CapsuleBlurEffectView() + var blurEffectView = CapsuleBlurEffectView(width: FrameResource.listCapsuleCellWidth) - lazy var lockImageView = { - let lockImageView = UIImageView() - lockImageView.image = .lock - lockImageView.tintColor = .themeGray200 - return lockImageView - }() - - lazy var dateLabel = { - let dateLabel = ThemeLabel(size: FrameResource.fontSize60, color: .themeGray200) + lazy var lockImageView = LockImageView() + lazy var closedDateLabel = { + let dateLabel = ThemeLabel(size: FrameResource.fontSize80, color: .themeGray200) dateLabel.textAlignment = .center return dateLabel }() @@ -48,10 +42,8 @@ final class ListCapsuleCell: UICollectionViewCell, UnOpenable { override func prepareForReuse() { super.prepareForReuse() - thumbnailImageView.imageView.image = nil - thumbnailImageView.imageView.subviews.forEach { - $0.removeFromSuperview() - } + + removeUnopenableEffect(superview: thumbnailImageView) } func addSubviews() { @@ -75,13 +67,17 @@ final class ListCapsuleCell: UICollectionViewCell, UnOpenable { } func configure(capsuleCellItem: ListCapsuleCellItem) { - if let thumbnailURL = capsuleCellItem.thumbnailImageURL { - thumbnailImageView.imageView.kr.setImage(with: thumbnailURL, placeholder: .empty, scale: FrameResource.openableImageScale) - } + thumbnailImageView.imageView.kr.setImage( + with: capsuleCellItem.thumbnailImageURL, + placeholder: .empty, + scale: FrameResource.openableImageScale + ) + descriptionLabel.text = "\(capsuleCellItem.memoryDate.dateString)\n\(capsuleCellItem.address)에서" - dateLabel.text = "밀봉시간: \(capsuleCellItem.closedDate.dateTimeString)" - if !capsuleCellItem.isOpenable() { - applyUnOpenableEffect() + closedDateLabel.text = "밀봉시간:\(capsuleCellItem.closedDate.dateString)" + + if !capsuleCellItem.isOpenable { + applyUnopenableEffect(superview: thumbnailImageView) } } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCellItem.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCellItem.swift index 9ecda09..b4a7a85 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCellItem.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/ListCapsuleCellItem.swift @@ -8,18 +8,24 @@ import CoreLocation import UIKit -struct ListCapsuleCellItem: Hashable, Equatable { +struct ListCapsuleCellItem { let uuid: String - let thumbnailImageURL: String? + let thumbnailImageURL: String let address: String let closedDate: Date let memoryDate: Date let coordinate: CLLocationCoordinate2D - func isOpenable() -> Bool { - return LocationManager.shared.isOpenable(capsuleCoordinate: coordinate) ? true : false + var isOpenable: Bool { + LocationManager.shared.isOpenable(capsuleCoordinate: coordinate) } + func distance() -> Double { + return LocationManager.shared.distance(capsuleCoordinate: coordinate) + } +} + +extension ListCapsuleCellItem: Hashable { static func == (lhs: Self, rhs: Self) -> Bool { return lhs.uuid == rhs.uuid } @@ -27,8 +33,4 @@ struct ListCapsuleCellItem: Hashable, Equatable { func hash(into hasher: inout Hasher) { hasher.combine(uuid) } - - func distance() -> Double { - return LocationManager.shared.distance(capsuleCoordinate: coordinate) - } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/LockImageView.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/LockImageView.swift new file mode 100644 index 0000000..02dab7a --- /dev/null +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/LockImageView.swift @@ -0,0 +1,20 @@ +// +// LockImageView.swift +// SpaceCapsule +// +// Created by 장재훈 on 2022/12/15. +// + +import UIKit + +final class LockImageView: UIImageView { + init() { + super.init(image: .lock) + tintColor = .themeGray200 + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/UnOpenable.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/UnOpenable.swift index 8f21f2c..2b2e9ac 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/UnOpenable.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/CapsuleList/Components/UnOpenable.swift @@ -9,32 +9,41 @@ import SnapKit import UIKit protocol UnOpenable: UIView { - var thumbnailImageView: ThemeThumbnailImageView { get } var blurEffectView: CapsuleBlurEffectView { get } - var lockImageView: UIImageView { get } - var dateLabel: ThemeLabel {get} - func applyUnOpenableEffect() + var lockImageView: LockImageView { get } + var closedDateLabel: ThemeLabel { get } + + func applyUnopenableEffect(superview: UIView) + func removeUnopenableEffect(superview: UIView) } extension UnOpenable { - func applyUnOpenableEffect() { - [blurEffectView, lockImageView, dateLabel].forEach { [weak self] in - self?.thumbnailImageView.imageView.addSubview($0) + func applyUnopenableEffect(superview: UIView) { + [blurEffectView, lockImageView, closedDateLabel].forEach { + superview.addSubview($0) } - + blurEffectView.snp.makeConstraints { $0.center.equalToSuperview() $0.width.height.equalToSuperview() } - + lockImageView.snp.makeConstraints { $0.center.equalToSuperview() - $0.width.height.equalTo(thumbnailImageView.snp.width).multipliedBy(0.3) + $0.width.height.equalTo(superview.snp.width).multipliedBy(0.3) } - - dateLabel.snp.makeConstraints { + + closedDateLabel.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(lockImageView.snp.bottom).offset(FrameResource.verticalPadding) } } + + func removeUnopenableEffect(superview: UIView) { + superview.subviews.forEach { + if $0 is CapsuleBlurEffectView || $0 is LockImageView || $0 is ThemeLabel { + $0.removeFromSuperview() + } + } + } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCell.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCell.swift index 4e7b5e7..080d1c2 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCell.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCell.swift @@ -9,10 +9,10 @@ import KingReceiver import SnapKit import UIKit -final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { +final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { var uuid: String? - - var thumbnailImageView = ThemeThumbnailImageView(frame: .zero, width: FrameResource.homeCapsuleCellWidth) + + var thumbnailImageView = ThumbnailImageView(frame: .zero, width: FrameResource.homeCapsuleCellWidth) var titleLabel = { let label = ThemeLabel(size: FrameResource.fontSize120, color: .themeColor200) @@ -20,32 +20,28 @@ final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { label.textAlignment = .center return label }() - + var descriptionLabel = { let label = ThemeLabel(size: FrameResource.fontSize100, color: .themeGray300) label.numberOfLines = 3 label.textAlignment = .center return label }() - - var dateLabel = { - let dateLabel = ThemeLabel(size: FrameResource.fontSize80, color: .themeGray200) + + var closedDateLabel = { + let dateLabel = ThemeLabel(size: FrameResource.fontSize100, color: .themeGray200) dateLabel.textAlignment = .center + return dateLabel }() - - var blurEffectView = CapsuleBlurEffectView() - - var lockImageView = { - let lockImageView = UIImageView() - lockImageView.image = .lock - lockImageView.tintColor = .themeGray200 - return lockImageView - }() - + + var blurEffectView = CapsuleBlurEffectView(width: FrameResource.homeCapsuleCellWidth) + + var lockImageView = LockImageView() + override init(frame: CGRect) { super.init(frame: frame) - + addSubviews() makeConstraints() } @@ -54,14 +50,13 @@ final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func prepareForReuse() { super.prepareForReuse() - thumbnailImageView.imageView.subviews.forEach { - $0.removeFromSuperview() - } + + removeUnopenableEffect(superview: thumbnailImageView) } - + func addSubviews() { [thumbnailImageView, titleLabel, descriptionLabel].forEach { self.contentView.addSubview($0) @@ -75,12 +70,12 @@ final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { $0.width.equalTo(FrameResource.homeCapsuleCellWidth) $0.height.equalTo(FrameResource.homeCapsuleCellThumbnailHeight) } - + titleLabel.snp.makeConstraints { $0.top.equalTo(thumbnailImageView.snp.bottom).offset(FrameResource.verticalPadding * 2) $0.centerX.equalToSuperview() } - + descriptionLabel.snp.makeConstraints { $0.top.equalTo(titleLabel.snp.bottom).offset(FrameResource.verticalPadding) $0.centerX.equalToSuperview() @@ -89,16 +84,19 @@ final class HomeCapsuleCell: UICollectionViewCell, UnOpenable { func configure(capsuleCellModel: HomeCapsuleCellItem) { uuid = capsuleCellModel.uuid - - if let thumbnailURL = capsuleCellModel.thumbnailImageURL { - thumbnailImageView.imageView.kr.setImage(with: thumbnailURL, placeholder: .empty, scale: FrameResource.openableImageScale) - } - dateLabel.text = "밀봉시간: \(capsuleCellModel.closedDate.dateTimeString)" + + thumbnailImageView.imageView.kr.setImage( + with: capsuleCellModel.thumbnailImageURL, + placeholder: .empty, + scale: FrameResource.openableImageScale + ) + + closedDateLabel.text = "밀봉시간:\(capsuleCellModel.closedDate.dateString)" titleLabel.text = capsuleCellModel.type.title - descriptionLabel.text = capsuleCellModel.description() - - if !capsuleCellModel.isOpenable() { - applyUnOpenableEffect() + descriptionLabel.text = capsuleCellModel.description + + if !capsuleCellModel.isOpenable { + applyUnopenableEffect(superview: thumbnailImageView) } } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCellItem.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCellItem.swift index 3ceb94c..8d4d29b 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCellItem.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/Components/HomeCapsuleCellItem.swift @@ -5,8 +5,8 @@ // Created by 김민중 on 2022/11/29. // -import UIKit import CoreLocation +import UIKit enum CapsuleType: CaseIterable { case nearest @@ -17,7 +17,7 @@ enum CapsuleType: CaseIterable { case memoryOldest case leastOpened case mostOpened - + var title: String { switch self { case .nearest: @@ -38,35 +38,61 @@ enum CapsuleType: CaseIterable { return "열어본 횟수가 가장 많은 캡슐" } } + + func capsule(from capsules: [Capsule]) -> Capsule? { + switch self { + case .closedLongest: + return capsules.min { $0.closedDate < $1.closedDate } + + case .closedShortest: + return capsules.min { $0.closedDate > $1.closedDate } + + case .memoryOldest: + return capsules.min { $0.memoryDate < $1.memoryDate } + + case .memoryNewest: + return capsules.min { $0.memoryDate > $1.memoryDate } + + case .leastOpened: + return capsules.min { $0.openCount < $1.openCount } + + case .mostOpened: + return capsules.min { $0.openCount > $1.openCount } + + case .nearest: + return capsules.min { + let first = LocationManager.shared.distance(capsuleCoordinate: $0.geopoint.coordinate) + let second = LocationManager.shared.distance(capsuleCoordinate: $1.geopoint.coordinate) + + return first < second + } + + case .farthest: + return capsules.min { + let first = LocationManager.shared.distance(capsuleCoordinate: $0.geopoint.coordinate) + let second = LocationManager.shared.distance(capsuleCoordinate: $1.geopoint.coordinate) + + return first > second + } + } + } } -struct HomeCapsuleCellItem: Hashable, Equatable { +struct HomeCapsuleCellItem { let uuid: String - let thumbnailImageURL: String? + let thumbnailImageURL: String let address: String let closedDate: Date let memoryDate: Date let openCount: Int let coordinate: CLLocationCoordinate2D let type: CapsuleType - - func isOpenable() -> Bool { - return LocationManager.shared.isOpenable(capsuleCoordinate: coordinate) ? true : false - } - - static func == (lhs: Self, rhs: Self) -> Bool { - return lhs.uuid == rhs.uuid - } - - func hash(into hasher: inout Hasher) { - hasher.combine(uuid) - } - - func distance() -> Double { - return LocationManager.shared.distance(capsuleCoordinate: coordinate) + + var isOpenable: Bool { + LocationManager.shared.isOpenable(capsuleCoordinate: coordinate) } - - func description() -> String { + + var description: String { switch type { case .closedLongest, .closedShortest: if let dDay = Calendar.current.numberOfDaysBetween(closedDate, and: Date()) { @@ -83,7 +109,7 @@ struct HomeCapsuleCellItem: Hashable, Equatable { case .nearest, .farthest: let distance = LocationManager.shared.distance(capsuleCoordinate: coordinate) if distance > 1000 { - return "\(memoryDate.dotDateString) \(address)에서\n약 \(String(format: "%.2f", (distance / 1000.0)))km" + return "\(memoryDate.dotDateString) \(address)에서\n약 \(String(format: "%.2f", distance / 1000.0))km" } else { return "\(memoryDate.dotDateString) \(address)에서\n약 \(String(format: "%.2f", distance))m" } @@ -92,3 +118,13 @@ struct HomeCapsuleCellItem: Hashable, Equatable { } } } + +extension HomeCapsuleCellItem: Hashable { + static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.uuid == rhs.uuid + } + + func hash(into hasher: inout Hasher) { + hasher.combine(uuid) + } +} diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/HomeViewModel.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/HomeViewModel.swift index bb7f162..6f00b2b 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/HomeViewModel.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Home/HomeViewModel.swift @@ -44,7 +44,7 @@ final class HomeViewModel: BaseViewModel, CapsuleCellNeedable { owner.coordinator?.tabBarAppearance(isHidden: false) }) .disposed(by: disposeBag) - + input.capsules .withUnretained(self) .subscribe( @@ -54,8 +54,8 @@ final class HomeViewModel: BaseViewModel, CapsuleCellNeedable { owner.output.userCapsuleStatus.accept(status) owner.output.featuredCapsuleCellItems.accept( - CapsuleType.allCases - .map { owner.getHomeCapsuleCellItem(capsules: capsuleList, type: $0) } + CapsuleType.allCases.shuffled() + .map { owner.homeCapsuleCellItem(capsules: capsuleList, type: $0) } .compactMap({ $0 }) ) @@ -85,65 +85,20 @@ final class HomeViewModel: BaseViewModel, CapsuleCellNeedable { .disposed(by: disposeBag) } - private func getHomeCapsuleCellItem(capsules: [Capsule], type: CapsuleType) -> HomeCapsuleCellItem? { - guard let capsule = getCapsule(in: capsules, by: type) else { + func homeCapsuleCellItem(capsules: [Capsule], type: CapsuleType) -> HomeCapsuleCellItem? { + guard let capsule = type.capsule(from: capsules) else { return nil } - + return HomeCapsuleCellItem( uuid: capsule.uuid, - thumbnailImageURL: capsule.images.first, + thumbnailImageURL: capsule.images.first ?? "", address: capsule.simpleAddress, closedDate: capsule.closedDate, memoryDate: capsule.memoryDate, openCount: capsule.openCount, - coordinate: CLLocationCoordinate2D( - latitude: capsule.geopoint.latitude, - longitude: capsule.geopoint.longitude - ), + coordinate: capsule.geopoint.coordinate, type: type ) } - - private func getCapsule(in capsules: [Capsule], by type: CapsuleType) -> Capsule? { - switch type { - case .closedLongest: - return capsules.min { $0.closedDate < $1.closedDate } - case .closedShortest: - return capsules.max { $0.closedDate < $1.closedDate } - case .memoryOldest: - return capsules.min { $0.memoryDate < $1.memoryDate } - case .memoryNewest: - return capsules.max { $0.memoryDate < $1.memoryDate } - case .nearest: - return orderCapsulesByDistance(capsules).first - case .farthest: - return orderCapsulesByDistance(capsules).last - case .leastOpened: - return capsules.min(by: { $0.openCount < $1.openCount }) - case .mostOpened: - return capsules.max(by: { $0.openCount < $1.openCount }) - } - } - - private func orderCapsulesByDistance(_ capsules: [Capsule]) -> [Capsule] { - let orderedCapsules = capsules.sorted { first, second in - let firstLocation = CLLocationCoordinate2D( - latitude: first.geopoint.latitude, - longitude: first.geopoint.longitude - ) - let secondLocation = CLLocationCoordinate2D( - latitude: second.geopoint.latitude, - longitude: second.geopoint.longitude - ) - let firstDistance = LocationManager.shared.distance(capsuleCoordinate: firstLocation) - let secondDistance = LocationManager.shared.distance(capsuleCoordinate: secondLocation) - return firstDistance < secondDistance - } - return orderedCapsules - } - - func getMostOpened(capsules: [Capsule]) -> Capsule? { - return capsules.min(by: { $0.openCount > $1.openCount }) - } } diff --git a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Profile/ProfileViewController.swift b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Profile/ProfileViewController.swift index e01ff78..89f4bf8 100644 --- a/SpaceCapsule/SpaceCapsule/Scene/TabBar/Profile/ProfileViewController.swift +++ b/SpaceCapsule/SpaceCapsule/Scene/TabBar/Profile/ProfileViewController.swift @@ -29,6 +29,7 @@ final class ProfileViewController: UIViewController, BaseViewController { var viewModel: ProfileViewModel? let profileView = ProfileView() fileprivate var currentNonce: String? + override func loadView() { view = profileView }