Skip to content

Commit

Permalink
feat : 선택된 사진들을 좌우 스와이프가 가능한 CollectionView 로 생성 #7
Browse files Browse the repository at this point in the history
- SelectedPhotoCell 생성
- 스와이프 페이지 인덱싱 적용
- 네비게이션바에 "다음" 버튼 추가
  • Loading branch information
wongbingg committed Aug 8, 2023
1 parent 8cbc854 commit 14320e3
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ protocol SelectSentencePresentableListener: AnyObject {

func showEditSentenceModal(with text: String)
func backButtonTapped()
func nextButtonTapped()
}

final class SelectSentenceViewController: UIViewController, SelectSentencePresentable, SelectSentenceViewControllable {
Expand All @@ -31,6 +32,9 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen
static let height = 44
static let backButtonLeftMargin = 8
static let nextButtonRightMargin = 8
enum CollectionViewCell {
static let width = DeviceSize.width
static let height = DeviceSize.height - Metric.NavigationBar.height - InfoBox.height - 100
}
}

Expand All @@ -49,6 +53,14 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen
return button
}()

private let nextButton: UIButton = {
let button = UIButton()
button.setTitle("다음", for: .normal)
button.setTitleColor(.systemGray, for: .disabled)
button.setTitleColor(.particleColor.main, for: .normal)
return button
}()

private let navigationTitle: UILabel = {
let label = UILabel()
label.text = "문장 선택 1/7"
Expand All @@ -74,6 +86,21 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
private let selectedPhotoCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(
width: Metric.CollectionViewCell.width,
height: Metric.CollectionViewCell.height
)
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(SelectedPhotoCell.self)
collectionView.backgroundColor = .init(hex: 0x1f1f1f)
return collectionView
}()

private let textView: UITextView = {
Expand Down Expand Up @@ -122,6 +149,7 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen
setConstraints()
setupNavigationBar()
configureTextView()
bind()
}

private func setupNavigationBar() {
Expand All @@ -131,23 +159,68 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen
self?.listener?.backButtonTapped()
}
.disposed(by: disposeBag)

nextButton.rx.tap
.bind { [weak self] in
self?.listener?.nextButtonTapped()
}
.disposed(by: disposeBag)

// TODO: 각 사진에서 문장추출이 모두 완료되었을 때 nextButton 활성화
// nextButton.isEnabled = false
}

private func configureTextView() {
textView.delegate = self
private func bind() {

bindCollectionViewCell()
bindPageIndex()
}

private func addCustomMenuItem() {
let menuItem1 = UIMenuItem(title: "문장뽑기", action: #selector(textSelected(_:)))
UIMenuController.shared.menuItems = nil
UIMenuController.shared.menuItems = [menuItem1]
private func bindCollectionViewCell() {
Observable.of(selectedImages)
.bind(to: selectedPhotoCollectionView.rx.items(
cellIdentifier: SelectedPhotoCell.defaultReuseIdentifier,
cellType: SelectedPhotoCell.self)
) { [weak self] index, item, cell in
cell.setImage(with: item)
cell.listener = self
}
.disposed(by: disposeBag)
}

@objc private func textSelected(_ sender: UIMenuController) {
if let selectedRange = textView.selectedTextRange {
let selectedText = textView.text(in: selectedRange) ?? "선택된 문장이 없습니다."
listener?.showEditSentenceModal(with: selectedText)
}
private func bindPageIndex() {
selectedPhotoCollectionView
.rx
.contentOffset
.subscribe { [weak self] point in
guard let self = self, let positionX = point.element?.x else { return }
switch positionX {
case (0..<DeviceSize.width/2):
self.navigationTitle.text = "문장 선택 1/\(self.selectedImages.count)"
case (DeviceSize.width/2..<DeviceSize.width*(3/2)):
self.navigationTitle.text = "문장 선택 2/\(self.selectedImages.count)"
case (DeviceSize.width*(3/2)..<DeviceSize.width*(5/2)):
self.navigationTitle.text = "문장 선택 3/\(self.selectedImages.count)"
case (DeviceSize.width*(5/2)..<DeviceSize.width*(7/2)):
self.navigationTitle.text = "문장 선택 4/\(self.selectedImages.count)"
case (DeviceSize.width*(7/2)..<DeviceSize.width*(9/2)):
self.navigationTitle.text = "문장 선택 5/\(self.selectedImages.count)"
default:
return
}
}
.disposed(by: disposeBag)
}

func recognizeTextImage(_ image: UIImage?) {
Expand Down Expand Up @@ -214,6 +287,7 @@ final class SelectSentenceViewController: UIViewController, SelectSentencePresen

// MARK: - UITextViewDelegate
extension SelectSentenceViewController: UITextViewDelegate {
extension SelectSentenceViewController: SelectedPhotoCellListener {

func textViewDidChangeSelection(_ textView: UITextView) {
let selectedRange = textView.selectedRange
Expand All @@ -231,11 +305,11 @@ extension SelectSentenceViewController: UITextViewDelegate {
private extension SelectSentenceViewController {

func addSubviews() {
[backButton, navigationTitle].forEach {
[backButton, navigationTitle, nextButton].forEach {
navigationBar.addSubview($0)
}

[navigationBar, infoBox, textView].forEach {
[navigationBar, infoBox, selectedPhotoCollectionView].forEach {
view.addSubview($0)
}

Expand All @@ -253,6 +327,11 @@ private extension SelectSentenceViewController {
$0.left.equalToSuperview().inset(Metric.NavigationBar.backButtonLeftMargin)
}

nextButton.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.right.equalToSuperview().inset(Metric.NavigationBar.nextButtonRightMargin)
}

navigationTitle.snp.makeConstraints {
$0.center.equalToSuperview()
}
Expand All @@ -269,6 +348,7 @@ private extension SelectSentenceViewController {
}

textView.snp.makeConstraints {
selectedPhotoCollectionView.snp.makeConstraints {
$0.top.equalTo(infoBox.snp.bottom)
$0.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// SelectedPhotoCell.swift
// Particle
//
// Created by 이원빈 on 2023/07/30.
//

import UIKit
import Photos
import VisionKit

protocol SelectedPhotoCellListener: AnyObject {
func copyButtonTapped(with text: String)
}

final class SelectedPhotoCell: UICollectionViewCell {

private let mainScrollView: UIScrollView = {
let scrollView = UIScrollView()
return scrollView
}()

private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()

private lazy var interaction: ImageAnalysisInteraction = {
let interaction = ImageAnalysisInteraction()
interaction.preferredInteractionTypes = .automatic
interaction.allowLongPressForDataDetectorsInTextMode = true
return interaction
}()

private let imageAnalyzer = ImageAnalyzer()

private var copiedText = ""
weak var listener: SelectedPhotoCellListener?

override init(frame: CGRect) {
super.init(frame: frame)
addSubviews()
setConstraints()
contentView.clipsToBounds = true
NotificationCenter.default.addObserver(
self,
selector: #selector(copyButtonTapped),
name: UIPasteboard.changedNotification,
object: nil
)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

deinit {
NotificationCenter.default.removeObserver(self)
}

override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
}

private func showLiveText() {
guard let image = imageView.image else {
Console.error("imageView.image == nil 입니다.")
return
}

Task {
let configuration = ImageAnalyzer.Configuration([.text])

do {
let analysis = try await imageAnalyzer.analyze(image, configuration: configuration)

DispatchQueue.main.async {
self.interaction.analysis = nil
self.interaction.preferredInteractionTypes = []

self.interaction.analysis = analysis
self.interaction.preferredInteractionTypes = .textSelection
}
} catch {
Console.error(error.localizedDescription)
}
}
}


@objc func copyButtonTapped() {
if let theString = UIPasteboard.general.string {
copiedText = theString
Console.log(copiedText)
listener?.copyButtonTapped(with: copiedText)
}
}

func setImage(with asset: PHAsset) {
imageView.addInteraction(interaction)

imageView.fetchImage(
asset: asset,
contentMode: .default,
targetSize: imageView.frame.size
) { [weak self] aspectRatio in
self?.imageView.snp.makeConstraints {
$0.width.equalTo(DeviceSize.width)
$0.height.equalTo(aspectRatio * DeviceSize.width)
}
self?.showLiveText()
}
}
}

// MARK: - Layout Settting

private extension SelectedPhotoCell {

func addSubviews() {
contentView.addSubview(mainScrollView)
mainScrollView.addSubview(imageView)
}

func setConstraints() {
mainScrollView.snp.makeConstraints {
$0.top.bottom.leading.trailing.equalTo(contentView.safeAreaLayoutGuide)
}

imageView.snp.makeConstraints {
$0.top.bottom.leading.trailing.equalTo(mainScrollView)
}
}
}

0 comments on commit 14320e3

Please sign in to comment.