diff --git a/StreetDrop/ShareExtension/View/DropDone/DropDoneView.swift b/StreetDrop/ShareExtension/View/DropDone/DropDoneView.swift new file mode 100644 index 0000000..439db02 --- /dev/null +++ b/StreetDrop/ShareExtension/View/DropDone/DropDoneView.swift @@ -0,0 +1,245 @@ +// +// DropDoneView.swift +// ShareExtension +// +// Created by 차요셉 on 8/19/24. +// + +import UIKit + +import Kingfisher +import SnapKit + +final class DropDoneView: UIView { + private let droppedMusic: Music + private let droppedAddress: String + private let droppedComment: String + + init( + droppedMusic: Music, + droppedAddress: String, + droppedComment: String + ) { + self.droppedMusic = droppedMusic + self.droppedAddress = droppedAddress + self.droppedComment = droppedComment + super.init(frame: .zero) + configureUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private let dropDoneTitleLabel: UILabel = { + let label: UILabel = .init() + label.text = "드랍 완료!" + label.font = .pretendard(size: 18, weight: 700) + label.textColor = .white + label.setLineHeight(lineHeight: 28) + + return label + }() + + private let exitButton: UIButton = { + let button: UIButton = .init() + button.setImage(.init(named: "exit"), for: .normal) + + return button + }() + + private let contentContainerView: UIView = { + let view: UIView = .init() + view.backgroundColor = .gray700 + view.layer.cornerRadius = 20 + + return view + }() + + private lazy var albumImageView: UIImageView = { + let imageView: UIImageView = .init() + imageView.layer.cornerRadius = 8 + imageView.layer.borderWidth = 1 + imageView.layer.borderColor = UIColor.white.withAlphaComponent(0.05).cgColor + imageView.layer.masksToBounds = true + + imageView.kf.setImage(with: URL(string: droppedMusic.albumImage)) + + return imageView + }() + + private lazy var songNameLabel: UILabel = { + let label: UILabel = .init() + label.text = droppedMusic.songName + label.font = .pretendard(size: 14, weight: 500) + label.textColor = .white + label.setLineHeight(lineHeight: 20) + + return label + }() + + private lazy var artistNameLabel: UILabel = { + let label: UILabel = .init() + label.text = droppedMusic.artistName + label.textColor = .gray200 + label.font = .pretendard(size: 12, weight: 400) + label.setLineHeight(lineHeight: 16) + + return label + }() + + private let middleLine: UIView = { + let view: UIView = .init() + view.backgroundColor = .gray600 + + return view + }() + + private lazy var commentLabel: UILabel = { + let label: UILabel = .init() + label.text = droppedComment + label.font = .pretendard(size: 14, weight: 400) + label.textColor = .white + label.numberOfLines = 0 + label.setLineHeight(lineHeight: 20) + + return label + }() + + private let locationImageView: UIImageView = { + let imageView: UIImageView = .init(image: .init(named: "share-extension-location")) + + return imageView + }() + + private lazy var addressLabel: UILabel = { + let label: UILabel = .init() + label.text = droppedAddress + label.font = .pretendard(size: 12, weight: 400) + label.textColor = .primary400 + label.setLineHeight(lineHeight: 16) + + return label + }() + + private let viewOnAppButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("앱에서 보기", for: .normal) + button.setTitleColor(.gray900, for: .normal) + button.titleLabel?.font = .pretendard(size: 16, weight: 700) + button.layer.cornerRadius = 12 + button.backgroundColor = .primary400 + + return button + }() +} + +private extension DropDoneView { + func configureUI() { + layer.cornerRadius = 20 + layer.borderWidth = 1 + layer.borderColor = UIColor.gray600.cgColor + backgroundColor = .gray800 + + [ + dropDoneTitleLabel, + exitButton, + contentContainerView, + viewOnAppButton + ].forEach { + addSubview($0) + } + + [ + albumImageView, + songNameLabel, + artistNameLabel, + middleLine, + commentLabel, + locationImageView, + addressLabel + ].forEach { + contentContainerView.addSubview($0) + } + + dropDoneTitleLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(22) + $0.leading.equalToSuperview().inset(24) + $0.height.equalTo(28) + } + + exitButton.snp.makeConstraints { + $0.width.equalTo(44) + $0.height.equalTo(32) + $0.top.equalToSuperview().inset(20) + $0.trailing.equalToSuperview().inset(24) + } + + var contentContainerViewHeightConstraint: Constraint? + contentContainerView.snp.makeConstraints { + contentContainerViewHeightConstraint = $0.height.equalTo(0).constraint + $0.top.equalTo(dropDoneTitleLabel.snp.bottom).offset(10) + $0.horizontalEdges.equalToSuperview().inset(24) + } + + albumImageView.snp.makeConstraints { + $0.width.height.equalTo(48) + $0.top.equalToSuperview().inset(16) + $0.leading.equalToSuperview().inset(20) + } + + songNameLabel.snp.makeConstraints { + $0.height.equalTo(20) + $0.top.equalToSuperview().inset(20) + $0.leading.equalTo(albumImageView.snp.trailing).offset(8) + } + + artistNameLabel.snp.makeConstraints { + $0.height.equalTo(16) + $0.top.equalTo(songNameLabel.snp.bottom).offset(2) + $0.leading.equalTo(albumImageView.snp.trailing).offset(8) + } + + middleLine.snp.makeConstraints { + $0.height.equalTo(1) + $0.top.equalTo(albumImageView.snp.bottom).offset(16) + $0.horizontalEdges.equalToSuperview().inset(20) + } + + commentLabel.snp.makeConstraints { + $0.top.equalTo(middleLine.snp.bottom).offset(16) + $0.horizontalEdges.equalToSuperview().inset(20) + } + + locationImageView.snp.makeConstraints { + $0.width.equalTo(13) + $0.height.equalTo(16) + $0.top.equalTo(commentLabel.snp.bottom).offset(10) + $0.leading.equalToSuperview().inset(20) + } + + addressLabel.snp.makeConstraints { + $0.height.equalTo(16) + $0.top.equalTo(locationImageView) + $0.leading.equalTo(locationImageView.snp.trailing) + } + + viewOnAppButton.snp.makeConstraints { + $0.height.equalTo(56) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalToSuperview().inset(48) + } + + layoutIfNeeded() + + let contentContainerHeight = 16 + 48 + 33 + commentLabel.actualNumberOfLines() * 20 + 46 + contentContainerViewHeightConstraint?.update( + offset: contentContainerHeight + ) + + snp.makeConstraints { + $0.height.equalTo(60 + contentContainerHeight + 132) + } + } +} diff --git a/StreetDrop/ShareExtension/View/ShareViewController.swift b/StreetDrop/ShareExtension/View/ShareViewController.swift index d809b71..20cb1b8 100644 --- a/StreetDrop/ShareExtension/View/ShareViewController.swift +++ b/StreetDrop/ShareExtension/View/ShareViewController.swift @@ -363,6 +363,28 @@ private extension ShareViewController { owner.reSearchingMusicForSharingView.settingMusicDataRelay.accept(musicList) } .disposed(by: disposeBag) + + output.goDropDoneView + .bind(onNext: { (droppedMusic, droppedAddress, droppedComment) in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { [weak self] in + guard let self = self else { return } + containerView.isHidden = true + reSearchingMusicForSharingView.isHidden = true + + let dropDoneView: DropDoneView = .init( + droppedMusic: droppedMusic, + droppedAddress: droppedAddress, + droppedComment: droppedComment + ) + view.addSubview(dropDoneView) + dropDoneView.snp.makeConstraints { + $0.horizontalEdges.bottom.equalToSuperview() + } + + view.layoutIfNeeded() + }) + }) + .disposed(by: disposeBag) } func configureUI() { diff --git a/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift b/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift index d9eb94e..d003ac0 100644 --- a/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift +++ b/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift @@ -58,6 +58,10 @@ final class ShareViewModel: NSObject, ShareViewModelType { var showReSearchedMusicList: Observable<[Music]> { showReSearchedMusicListRelay.asObservable() } + fileprivate let goDropDoneViewRelay: PublishRelay<(Music, String, String)> = .init() + var goDropDoneView: Observable<(Music, String, String)> { + goDropDoneViewRelay.asObservable() + } } func convert(input: Input, disposedBag: DisposeBag) -> Output { @@ -122,9 +126,11 @@ final class ShareViewModel: NSObject, ShareViewModelType { ), content: comment ) - .subscribe { statusCode in - print("성공") - } onFailure: { error in + .subscribe(with: self) { owner, statusCode in + owner.output.goDropDoneViewRelay.accept( + (selectedMusic, currentLocation.address, comment) + ) + } onFailure: { owner, error in print(error.localizedDescription) } .disposed(by: disposedBag) diff --git a/StreetDrop/StreetDrop.xcodeproj/project.pbxproj b/StreetDrop/StreetDrop.xcodeproj/project.pbxproj index 973acb4..d6e87c4 100644 --- a/StreetDrop/StreetDrop.xcodeproj/project.pbxproj +++ b/StreetDrop/StreetDrop.xcodeproj/project.pbxproj @@ -437,6 +437,9 @@ C47F02242A38633C00F48884 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47F02232A38633C00F48884 /* SettingsViewModel.swift */; }; C47F02282A3864A500F48884 /* SettingElementCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47F02272A3864A500F48884 /* SettingElementCell.swift */; }; C47F022A2A3869DC00F48884 /* SettingSectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47F02292A3869DC00F48884 /* SettingSectionType.swift */; }; + C48B76F52C73151B0003BC3C /* DropDoneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48B76F42C73151B0003BC3C /* DropDoneView.swift */; }; + C48B76F72C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48B76F62C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift */; }; + C48B76F82C7339EB0003BC3C /* UILabel+actualNumberOfLines.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48B76F62C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift */; }; C49BA7612A1BDFC900A83E95 /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D16FC82A1B9338008B076F /* UIFont+Extension.swift */; }; C49BA7622A1BDFCC00A83E95 /* UILabel+LineHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D16FCB2A1B98B0008B076F /* UILabel+LineHeight.swift */; }; C49BA7832A1BE71700A83E95 /* SearchingMusicViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C434A4CB2A1796F300C63526 /* SearchingMusicViewModel.swift */; }; @@ -769,6 +772,8 @@ C47F02232A38633C00F48884 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; C47F02272A3864A500F48884 /* SettingElementCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingElementCell.swift; sourceTree = ""; }; C47F02292A3869DC00F48884 /* SettingSectionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSectionType.swift; sourceTree = ""; }; + C48B76F42C73151B0003BC3C /* DropDoneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDoneView.swift; sourceTree = ""; }; + C48B76F62C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+actualNumberOfLines.swift"; sourceTree = ""; }; C49EDACB2BBD75480025DB55 /* CongratulationsLevelUpPopUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CongratulationsLevelUpPopUpViewController.swift; sourceTree = ""; }; C49EDACD2BBD7EFE0025DB55 /* GradientLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientLabel.swift; sourceTree = ""; }; C4A0567A2A631D6E00AE2F0B /* CustomSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSwitch.swift; sourceTree = ""; }; @@ -1773,6 +1778,7 @@ children = ( C42182BC2C697F9700B73A99 /* ShareViewController.swift */, C44DE1FC2C6EE089004F211C /* ReSearchingMusic */, + C48B76F32C7314F20003BC3C /* DropDone */, ); path = View; sourceTree = ""; @@ -1978,6 +1984,14 @@ path = ViewModel; sourceTree = ""; }; + C48B76F32C7314F20003BC3C /* DropDone */ = { + isa = PBXGroup; + children = ( + C48B76F42C73151B0003BC3C /* DropDoneView.swift */, + ); + path = DropDone; + sourceTree = ""; + }; C4A4458E2A5EF203008279C1 /* FCM */ = { isa = PBXGroup; children = ( @@ -2093,6 +2107,7 @@ F4AA84DE2C1F3B0200CADB1A /* Array+Extension.swift */, 6A51EC3B2C3E52FE00DEF6F3 /* UIViewController+Rx.swift */, 6A51EC3D2C3E536000DEF6F3 /* Map+Rx.swift */, + C48B76F62C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift */, ); path = Extensions; sourceTree = ""; @@ -2623,6 +2638,7 @@ 41396D912A4EFBF700B69341 /* DefaultEditCommentRepository.swift in Sources */, C419722E2ABD9D6F00211222 /* MyInfoUseCase.swift in Sources */, C4A4E3BC2C6447AF00283C37 /* FetchingRegionFilteredDropCountUseCase.swift in Sources */, + C48B76F72C7339DA0003BC3C /* UILabel+actualNumberOfLines.swift in Sources */, C4A0567B2A631D6E00AE2F0B /* CustomSwitch.swift in Sources */, 6A26BF342B33D2210007B6B7 /* DefaultFetchingSingleMusicUseCase.swift in Sources */, 1876F04E2A66EE030064B887 /* MyLevel.swift in Sources */, @@ -2842,6 +2858,7 @@ C432DF002C69F7F3003DBA18 /* UIViewController+Rx.swift in Sources */, C432DF012C69F7F3003DBA18 /* Map+Rx.swift in Sources */, C432DF022C69F7F3003DBA18 /* UIFont+Extension.swift in Sources */, + C48B76F82C7339EB0003BC3C /* UILabel+actualNumberOfLines.swift in Sources */, C432DF032C69F7F3003DBA18 /* TestError.swift in Sources */, C432DF042C69F7F3003DBA18 /* ImageCacheError.swift in Sources */, C432DF052C69F7F3003DBA18 /* UserDefaultsError.swift in Sources */, @@ -2892,6 +2909,7 @@ C432DF322C69F7F3003DBA18 /* EditCommentRepository.swift in Sources */, C432DF332C69F7F3003DBA18 /* DeleteMusicRepository.swift in Sources */, C432DF342C69F7F3003DBA18 /* BlockUserRepository.swift in Sources */, + C48B76F52C73151B0003BC3C /* DropDoneView.swift in Sources */, C432DF352C69F7F3003DBA18 /* PopUpRepository.swift in Sources */, C432DF362C69F7F3003DBA18 /* DefaultMainRepository.swift in Sources */, C432DF372C69F7F3003DBA18 /* DefaultSearchingMusicRepository.swift in Sources */, diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/Contents.json b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/Contents.json new file mode 100644 index 0000000..0656e6c --- /dev/null +++ b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "share-extension-location.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "share-extension-location@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "share-extension-location@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location.png new file mode 100644 index 0000000..8670f97 Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location.png differ diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@2x.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@2x.png new file mode 100644 index 0000000..38d3c5a Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@2x.png differ diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@3x.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@3x.png new file mode 100644 index 0000000..a28d889 Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/share-extension-location.imageset/share-extension-location@3x.png differ diff --git a/StreetDrop/StreetDrop/Util/Extensions/UILabel+actualNumberOfLines.swift b/StreetDrop/StreetDrop/Util/Extensions/UILabel+actualNumberOfLines.swift new file mode 100644 index 0000000..74de069 --- /dev/null +++ b/StreetDrop/StreetDrop/Util/Extensions/UILabel+actualNumberOfLines.swift @@ -0,0 +1,52 @@ +// +// UILabel+actualNumberOfLines.swift +// StreetDrop +// +// Created by 차요셉 on 8/19/24. +// + +import UIKit + +extension UILabel { + func actualNumberOfLines() -> Int { + // 레이아웃이 완료된 이후에만 프레임을 사용 + layoutIfNeeded() + + guard let text = self.text, let font = self.font else { + return 0 + } + + // UILabel의 textContainer로 계산 + let textStorage = NSTextStorage(string: text) + let textContainer = NSTextContainer(size: CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude)) + let layoutManager = NSLayoutManager() + + layoutManager.addTextContainer(textContainer) + textStorage.addLayoutManager(layoutManager) + + textStorage.addAttribute(.font, value: font, range: NSRange(location: 0, length: textStorage.length)) + textContainer.lineFragmentPadding = 0.0 + textContainer.lineBreakMode = lineBreakMode + + layoutManager.glyphRange(for: textContainer) + + let numberOfLines = layoutManager.numberOfLines(in: textContainer) + return numberOfLines + } +} + +extension NSLayoutManager { + func numberOfLines(in textContainer: NSTextContainer) -> Int { + var numberOfLines = 0 + var index = 0 + var lineRange: NSRange = NSRange() + + while index < numberOfGlyphs { + self.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange) + index = NSMaxRange(lineRange) + numberOfLines += 1 + } + + return numberOfLines + } +}