metatext/Views/CompositionView.swift

144 lines
5.6 KiB
Swift
Raw Normal View History

2020-12-10 02:44:06 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import Kingfisher
import UIKit
2020-12-19 06:30:19 +00:00
final class CompositionView: UIView {
2020-12-10 02:44:06 +00:00
let avatarImageView = UIImageView()
let textView = UITextView()
2020-12-17 06:48:06 +00:00
let attachmentUploadView = AttachmentUploadView()
2020-12-19 06:30:19 +00:00
let attachmentsCollectionView: UICollectionView
2020-12-10 02:44:06 +00:00
private var compositionConfiguration: CompositionContentConfiguration
private var cancellables = Set<AnyCancellable>()
2020-12-19 06:30:19 +00:00
private lazy var attachmentsDataSource: CompositionAttachmentsDataSource = {
CompositionAttachmentsDataSource(
collectionView: attachmentsCollectionView,
viewModelProvider: compositionConfiguration.viewModel.attachmentViewModel(indexPath:))
}()
2020-12-10 02:44:06 +00:00
init(configuration: CompositionContentConfiguration) {
self.compositionConfiguration = configuration
2020-12-19 06:30:19 +00:00
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalWidth(0.2))
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item])
group.interItemSpacing = .fixed(.defaultSpacing)
let section = NSCollectionLayoutSection(group: group)
let attachmentsLayout = UICollectionViewCompositionalLayout(section: section)
attachmentsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: attachmentsLayout)
2020-12-10 02:44:06 +00:00
super.init(frame: .zero)
initialSetup()
applyCompositionConfiguration()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension CompositionView: UIContentView {
var configuration: UIContentConfiguration {
get { compositionConfiguration }
set {
guard let compositionConfiguration = newValue as? CompositionContentConfiguration else { return }
self.compositionConfiguration = compositionConfiguration
applyCompositionConfiguration()
}
}
}
2020-12-16 01:39:38 +00:00
extension CompositionView: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
2020-12-18 00:17:17 +00:00
compositionConfiguration.viewModel.text = textView.text
2020-12-16 01:39:38 +00:00
}
}
2020-12-10 02:44:06 +00:00
private extension CompositionView {
2020-12-19 06:30:19 +00:00
static let attachmentUploadViewHeight: CGFloat = 100
2020-12-17 06:48:06 +00:00
2020-12-10 02:44:06 +00:00
func initialSetup() {
addSubview(avatarImageView)
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
avatarImageView.layer.cornerRadius = .avatarDimension / 2
avatarImageView.clipsToBounds = true
let stackView = UIStackView()
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.addArrangedSubview(textView)
textView.isScrollEnabled = false
textView.adjustsFontForContentSizeCategory = true
textView.font = .preferredFont(forTextStyle: .body)
textView.textContainer.lineFragmentPadding = 0
2020-12-16 01:39:38 +00:00
textView.inputAccessoryView = CompositionInputAccessoryView(viewModel: compositionConfiguration.viewModel)
textView.inputAccessoryView?.sizeToFit()
textView.delegate = self
2020-12-10 02:44:06 +00:00
2020-12-19 06:30:19 +00:00
stackView.addArrangedSubview(attachmentsCollectionView)
attachmentsCollectionView.dataSource = attachmentsDataSource
2020-12-17 06:48:06 +00:00
stackView.addArrangedSubview(attachmentUploadView)
2020-12-12 00:41:37 +00:00
let constraints = [
2020-12-10 02:44:06 +00:00
avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
2020-12-12 00:41:37 +00:00
avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
2020-12-10 02:44:06 +00:00
avatarImageView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
avatarImageView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
avatarImageView.bottomAnchor.constraint(lessThanOrEqualTo: readableContentGuide.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: .defaultSpacing),
stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
2020-12-17 06:48:06 +00:00
stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
2020-12-19 06:30:19 +00:00
attachmentsCollectionView.heightAnchor.constraint(
equalTo: attachmentsCollectionView.widthAnchor,
multiplier: 1 / 4),
attachmentUploadView.heightAnchor.constraint(equalToConstant: Self.attachmentUploadViewHeight)
2020-12-12 00:41:37 +00:00
]
for constraint in constraints {
constraint.priority = .justBelowMax
}
NSLayoutConstraint.activate(constraints)
2020-12-19 23:05:14 +00:00
}
func applyCompositionConfiguration() {
cancellables.removeAll()
2020-12-10 02:44:06 +00:00
compositionConfiguration.viewModel.$identification.map(\.identity.image)
.sink { [weak self] in self?.avatarImageView.kf.setImage(with: $0) }
.store(in: &cancellables)
2020-12-17 06:48:06 +00:00
2020-12-19 06:30:19 +00:00
compositionConfiguration.viewModel.$attachmentViewModels
.sink { [weak self] in
self?.attachmentsDataSource.apply([$0.map(\.attachment)].snapshot())
self?.attachmentsCollectionView.isHidden = $0.isEmpty
}
.store(in: &cancellables)
2020-12-17 06:48:06 +00:00
compositionConfiguration.viewModel.$attachmentUpload
.sink { [weak self] in self?.attachmentUploadView.attachmentUpload = $0 }
.store(in: &cancellables)
2020-12-10 02:44:06 +00:00
}
}