forked from zelo72/mastodon-ios
feat: update iPad UI for compose scene
This commit is contained in:
parent
4af5023c4c
commit
14cefe2d0d
|
@ -11,12 +11,17 @@ import Combine
|
|||
import MetaTextKit
|
||||
import UITextView_Placeholder
|
||||
|
||||
protocol ComposeStatusContentTableViewCellDelegate: AnyObject {
|
||||
func composeStatusContentTableViewCell(_ cell: ComposeStatusContentTableViewCell, textViewShouldBeginEditing textView: UITextView) -> Bool
|
||||
}
|
||||
|
||||
final class ComposeStatusContentTableViewCell: UITableViewCell {
|
||||
|
||||
let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "UI")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
weak var delegate: ComposeStatusContentTableViewCellDelegate?
|
||||
|
||||
let statusView = ReplicaStatusView()
|
||||
|
||||
let statusContentWarningEditorView = StatusContentWarningEditorView()
|
||||
|
@ -136,6 +141,10 @@ extension ComposeStatusContentTableViewCell {
|
|||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension ComposeStatusContentTableViewCell: UITextViewDelegate {
|
||||
|
||||
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
|
||||
return delegate?.composeStatusContentTableViewCell(self, textViewShouldBeginEditing: textView) ?? true
|
||||
}
|
||||
|
||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
switch textView {
|
||||
|
|
|
@ -26,9 +26,20 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
var viewModel: ComposeViewModel!
|
||||
|
||||
let logger = Logger(subsystem: "ComposeViewController", category: "logic")
|
||||
|
||||
private var suffixedAttachmentViews: [UIView] = []
|
||||
|
||||
private(set) lazy var cancelBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
let characterCountLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 15, weight: .regular)
|
||||
label.text = "500"
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.accessibilityLabel = L10n.A11y.Plural.Count.inputLimitRemains(500)
|
||||
return label
|
||||
}()
|
||||
private(set) lazy var characterCountBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(customView: characterCountLabel)
|
||||
return barButtonItem
|
||||
}()
|
||||
let publishButton: UIButton = {
|
||||
let button = RoundedEdgesButton(type: .custom)
|
||||
button.setTitle(L10n.Scene.Compose.composeAction, for: .normal)
|
||||
|
@ -41,8 +52,6 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
button.adjustsImageWhenHighlighted = false
|
||||
return button
|
||||
}()
|
||||
|
||||
private(set) lazy var cancelBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
private(set) lazy var publishBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(customView: publishButton)
|
||||
return barButtonItem
|
||||
|
@ -136,17 +145,16 @@ extension ComposeViewController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let groups = [UIBarButtonItemGroup(barButtonItems: [
|
||||
composeToolbarView.mediaBarButtonItem,
|
||||
composeToolbarView.pollBarButtonItem,
|
||||
composeToolbarView.contentWarningBarButtonItem,
|
||||
composeToolbarView.visibilityBarButtonItem,
|
||||
], representativeItem: nil)]
|
||||
|
||||
tableView.inputAssistantItem.trailingBarButtonGroups = groups
|
||||
textEditorView()?.textView.inputAssistantItem.trailingBarButtonGroups = groups
|
||||
|
||||
configureNavigationBarTitleStyle()
|
||||
viewModel.traitCollectionDidChangePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.configureNavigationBarTitleStyle()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
viewModel.title
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] title in
|
||||
|
@ -164,6 +172,18 @@ extension ComposeViewController {
|
|||
.store(in: &disposeBag)
|
||||
navigationItem.leftBarButtonItem = cancelBarButtonItem
|
||||
navigationItem.rightBarButtonItem = publishBarButtonItem
|
||||
viewModel.traitCollectionDidChangePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard self.traitCollection.userInterfaceIdiom == .pad else { return }
|
||||
var items = [self.publishBarButtonItem]
|
||||
if self.traitCollection.horizontalSizeClass == .regular {
|
||||
items.append(self.characterCountBarButtonItem)
|
||||
}
|
||||
self.navigationItem.rightBarButtonItems = items
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
publishButton.addTarget(self, action: #selector(ComposeViewController.publishBarButtonItemPressed(_:)), for: .touchUpInside)
|
||||
|
||||
|
||||
|
@ -229,8 +249,12 @@ extension ComposeViewController {
|
|||
dependency: self
|
||||
)
|
||||
|
||||
viewModel.composeStatusContentTableViewCell.delegate = self
|
||||
|
||||
// update layout when keyboard show/dismiss
|
||||
view.layoutIfNeeded()
|
||||
|
||||
let keyboardHasShortcutBar = CurrentValueSubject<Bool, Never>(traitCollection.userInterfaceIdiom == .pad) // update default value later
|
||||
let keyboardEventPublishers = Publishers.CombineLatest3(
|
||||
KeyboardResponderService.shared.isShow,
|
||||
KeyboardResponderService.shared.state,
|
||||
|
@ -243,8 +267,16 @@ extension ComposeViewController {
|
|||
)
|
||||
.sink(receiveValue: { [weak self] keyboardEvents, isCustomEmojiComposing, autoCompleteInfo in
|
||||
guard let self = self else { return }
|
||||
|
||||
|
||||
let (isShow, state, endFrame) = keyboardEvents
|
||||
|
||||
switch self.traitCollection.userInterfaceIdiom {
|
||||
case .pad:
|
||||
keyboardHasShortcutBar.value = state != .floating
|
||||
default:
|
||||
keyboardHasShortcutBar.value = false
|
||||
}
|
||||
|
||||
let extraMargin: CGFloat = {
|
||||
var margin = self.composeToolbarView.frame.height
|
||||
if autoCompleteInfo != nil {
|
||||
|
@ -342,7 +374,7 @@ extension ComposeViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isMediaToolbarButtonEnabled in
|
||||
guard let self = self else { return }
|
||||
// self.composeToolbarView.mediaBarButtonItem.isEnabled = isMediaToolbarButtonEnabled
|
||||
self.composeToolbarView.mediaBarButtonItem.isEnabled = isMediaToolbarButtonEnabled
|
||||
self.composeToolbarView.mediaButton.isEnabled = isMediaToolbarButtonEnabled
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -352,7 +384,7 @@ extension ComposeViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isPollToolbarButtonEnabled in
|
||||
guard let self = self else { return }
|
||||
// self.composeToolbarView.pollBarButtonItem.isEnabled = isPollToolbarButtonEnabled
|
||||
self.composeToolbarView.pollBarButtonItem.isEnabled = isPollToolbarButtonEnabled
|
||||
self.composeToolbarView.pollButton.isEnabled = isPollToolbarButtonEnabled
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -366,12 +398,12 @@ extension ComposeViewController {
|
|||
guard let self = self else { return }
|
||||
guard isPollToolbarButtonEnabled else {
|
||||
let accessibilityLabel = L10n.Scene.Compose.Accessibility.appendPoll
|
||||
// self.composeToolbarView.pollBarButtonItem.accessibilityLabel = accessibilityLabel
|
||||
self.composeToolbarView.pollBarButtonItem.accessibilityLabel = accessibilityLabel
|
||||
self.composeToolbarView.pollButton.accessibilityLabel = accessibilityLabel
|
||||
return
|
||||
}
|
||||
let accessibilityLabel = isPollComposing ? L10n.Scene.Compose.Accessibility.removePoll : L10n.Scene.Compose.Accessibility.appendPoll
|
||||
// self.composeToolbarView.pollBarButtonItem.accessibilityLabel = accessibilityLabel
|
||||
self.composeToolbarView.pollBarButtonItem.accessibilityLabel = accessibilityLabel
|
||||
self.composeToolbarView.pollButton.accessibilityLabel = accessibilityLabel
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -381,7 +413,9 @@ extension ComposeViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] attachmentServices in
|
||||
guard let self = self else { return }
|
||||
self.composeToolbarView.mediaButton.isEnabled = attachmentServices.count < 4
|
||||
let isEnabled = attachmentServices.count < 4
|
||||
self.composeToolbarView.mediaBarButtonItem.isEnabled = isEnabled
|
||||
self.composeToolbarView.mediaButton.isEnabled = isEnabled
|
||||
self.resetImagePicker()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -391,7 +425,9 @@ extension ComposeViewController {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] isContentWarningComposing in
|
||||
guard let self = self else { return }
|
||||
self.composeToolbarView.contentWarningButton.accessibilityLabel = isContentWarningComposing ? L10n.Scene.Compose.Accessibility.disableContentWarning : L10n.Scene.Compose.Accessibility.enableContentWarning
|
||||
let accessibilityLabel = isContentWarningComposing ? L10n.Scene.Compose.Accessibility.disableContentWarning : L10n.Scene.Compose.Accessibility.enableContentWarning
|
||||
self.composeToolbarView.contentWarningBarButtonItem.accessibilityLabel = accessibilityLabel
|
||||
self.composeToolbarView.contentWarningButton.accessibilityLabel = accessibilityLabel
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
@ -404,6 +440,7 @@ extension ComposeViewController {
|
|||
.sink { [weak self] type, _ in
|
||||
guard let self = self else { return }
|
||||
let image = type.image(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
||||
self.composeToolbarView.visibilityBarButtonItem.image = image
|
||||
self.composeToolbarView.visibilityButton.setImage(image, for: .normal)
|
||||
self.composeToolbarView.activeVisibilityType.value = type
|
||||
}
|
||||
|
@ -415,16 +452,27 @@ extension ComposeViewController {
|
|||
guard let self = self else { return }
|
||||
let count = ComposeViewModel.composeContentLimit - characterCount
|
||||
self.composeToolbarView.characterCountLabel.text = "\(count)"
|
||||
self.characterCountLabel.text = "\(count)"
|
||||
let font: UIFont
|
||||
let textColor: UIColor
|
||||
let accessibilityLabel: String
|
||||
switch count {
|
||||
case _ where count < 0:
|
||||
self.composeToolbarView.characterCountLabel.font = .monospacedDigitSystemFont(ofSize: 24, weight: .bold)
|
||||
self.composeToolbarView.characterCountLabel.textColor = Asset.Colors.danger.color
|
||||
self.composeToolbarView.characterCountLabel.accessibilityLabel = L10n.A11y.Plural.Count.inputLimitExceeds(abs(count))
|
||||
font = .monospacedDigitSystemFont(ofSize: 24, weight: .bold)
|
||||
textColor = Asset.Colors.danger.color
|
||||
accessibilityLabel = L10n.A11y.Plural.Count.inputLimitExceeds(abs(count))
|
||||
default:
|
||||
self.composeToolbarView.characterCountLabel.font = .monospacedDigitSystemFont(ofSize: 15, weight: .regular)
|
||||
self.composeToolbarView.characterCountLabel.textColor = Asset.Colors.Label.secondary.color
|
||||
self.composeToolbarView.characterCountLabel.accessibilityLabel = L10n.A11y.Plural.Count.inputLimitRemains(count)
|
||||
font = .monospacedDigitSystemFont(ofSize: 15, weight: .regular)
|
||||
textColor = Asset.Colors.Label.secondary.color
|
||||
accessibilityLabel = L10n.A11y.Plural.Count.inputLimitRemains(count)
|
||||
}
|
||||
self.composeToolbarView.characterCountLabel.font = font
|
||||
self.composeToolbarView.characterCountLabel.textColor = textColor
|
||||
self.composeToolbarView.characterCountLabel.accessibilityLabel = accessibilityLabel
|
||||
self.characterCountLabel.font = font
|
||||
self.characterCountLabel.textColor = textColor
|
||||
self.characterCountLabel.accessibilityLabel = accessibilityLabel
|
||||
self.characterCountLabel.sizeToFit()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
@ -467,6 +515,18 @@ extension ComposeViewController {
|
|||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
configureToolbarDisplay(keyboardHasShortcutBar: keyboardHasShortcutBar.value)
|
||||
Publishers.CombineLatest(
|
||||
keyboardHasShortcutBar,
|
||||
viewModel.traitCollectionDidChangePublisher
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] keyboardHasShortcutBar, _ in
|
||||
guard let self = self else { return }
|
||||
self.configureToolbarDisplay(keyboardHasShortcutBar: keyboardHasShortcutBar)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
@ -578,11 +638,7 @@ extension ComposeViewController {
|
|||
}
|
||||
|
||||
private func showDismissConfirmAlertController() {
|
||||
let alertController = UIAlertController(
|
||||
title: L10n.Common.Alerts.DiscardPostContent.title,
|
||||
message: L10n.Common.Alerts.DiscardPostContent.message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
let discardAction = UIAlertAction(title: L10n.Common.Controls.Actions.discard, style: .destructive) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
|
@ -590,6 +646,7 @@ extension ComposeViewController {
|
|||
alertController.addAction(discardAction)
|
||||
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
alertController.popoverPresentationController?.barButtonItem = cancelBarButtonItem
|
||||
present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
@ -610,6 +667,37 @@ extension ComposeViewController {
|
|||
tableView.backgroundColor = theme.systemElevatedBackgroundColor
|
||||
composeToolbarBackgroundView.backgroundColor = theme.composeToolbarBackgroundColor
|
||||
}
|
||||
|
||||
private func setupInputAssistantItem(item: UITextInputAssistantItem) {
|
||||
let groups = [UIBarButtonItemGroup(barButtonItems: [
|
||||
composeToolbarView.mediaBarButtonItem,
|
||||
composeToolbarView.pollBarButtonItem,
|
||||
composeToolbarView.contentWarningBarButtonItem,
|
||||
composeToolbarView.visibilityBarButtonItem,
|
||||
], representativeItem: nil)]
|
||||
|
||||
item.trailingBarButtonGroups = groups
|
||||
}
|
||||
|
||||
private func configureToolbarDisplay(keyboardHasShortcutBar: Bool) {
|
||||
switch self.traitCollection.userInterfaceIdiom {
|
||||
case .pad:
|
||||
let shouldHideToolbar = keyboardHasShortcutBar && self.traitCollection.horizontalSizeClass == .regular
|
||||
self.composeToolbarView.alpha = shouldHideToolbar ? 0 : 1
|
||||
self.composeToolbarBackgroundView.alpha = shouldHideToolbar ? 0 : 1
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func configureNavigationBarTitleStyle() {
|
||||
switch traitCollection.userInterfaceIdiom {
|
||||
case .pad:
|
||||
navigationController?.navigationBar.prefersLargeTitles = traitCollection.horizontalSizeClass == .regular
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -661,6 +749,20 @@ extension ComposeViewController: MetaTextDelegate {
|
|||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension ComposeViewController: UITextViewDelegate {
|
||||
|
||||
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
|
||||
setupInputAssistantItem(item: textView.inputAssistantItem)
|
||||
return true
|
||||
}
|
||||
// func textViewDidBeginEditing(_ textView: UITextView) {
|
||||
// switch textView {
|
||||
// case textEditorView()?.textView:
|
||||
// setupInputAssistantItem(item: textView.inputAssistantItem)
|
||||
// default:
|
||||
// assertionFailure()
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
if textEditorView()?.textView === textView {
|
||||
|
@ -801,7 +903,7 @@ extension ComposeViewController: UITextViewDelegate {
|
|||
// MARK: - ComposeToolbarViewDelegate
|
||||
extension ComposeViewController: ComposeToolbarViewDelegate {
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: UIButton, mediaSelectionType type: ComposeToolbarView.MediaSelectionType) {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: Any, mediaSelectionType type: ComposeToolbarView.MediaSelectionType) {
|
||||
switch type {
|
||||
case .photoLibrary:
|
||||
present(photoLibraryPicker, animated: true, completion: nil)
|
||||
|
@ -812,7 +914,7 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: UIButton) {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: Any) {
|
||||
// toggle poll composing state
|
||||
viewModel.isPollComposing.value.toggle()
|
||||
|
||||
|
@ -834,11 +936,11 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: UIButton) {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: Any) {
|
||||
viewModel.isCustomEmojiComposing.value.toggle()
|
||||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: UIButton) {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: Any) {
|
||||
// cancel custom picker input
|
||||
viewModel.isCustomEmojiComposing.value = false
|
||||
|
||||
|
@ -858,7 +960,7 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: UIButton, visibilitySelectionType type: ComposeToolbarView.VisibilitySelectionType) {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: Any, visibilitySelectionType type: ComposeToolbarView.VisibilitySelectionType) {
|
||||
viewModel.selectedStatusVisibility.value = type
|
||||
}
|
||||
|
||||
|
@ -1048,6 +1150,9 @@ extension ComposeViewController: ComposeStatusAttachmentCollectionViewCellDelega
|
|||
extension ComposeViewController: ComposeStatusPollOptionCollectionViewCellDelegate {
|
||||
|
||||
func composeStatusPollOptionCollectionViewCell(_ cell: ComposeStatusPollOptionCollectionViewCell, textFieldDidBeginEditing textField: UITextField) {
|
||||
|
||||
setupInputAssistantItem(item: textField.inputAssistantItem)
|
||||
|
||||
// FIXME: make poll section visible
|
||||
// DispatchQueue.main.async {
|
||||
// self.collectionView.scroll(to: .bottom, animated: true)
|
||||
|
@ -1144,6 +1249,14 @@ extension ComposeViewController: ComposeStatusPollExpiresOptionCollectionViewCel
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - ComposeStatusContentTableViewCellDelegate
|
||||
extension ComposeViewController: ComposeStatusContentTableViewCellDelegate {
|
||||
func composeStatusContentTableViewCell(_ cell: ComposeStatusContentTableViewCell, textViewShouldBeginEditing textView: UITextView) -> Bool {
|
||||
setupInputAssistantItem(item: textView.inputAssistantItem)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AutoCompleteViewControllerDelegate
|
||||
extension ComposeViewController: AutoCompleteViewControllerDelegate {
|
||||
func autoCompleteViewController(_ viewController: AutoCompleteViewController, didSelectItem item: AutoCompleteItem) {
|
||||
|
|
|
@ -39,6 +39,24 @@ extension ComposeViewModel {
|
|||
|
||||
// setup data source
|
||||
tableView.dataSource = self
|
||||
|
||||
composeStatusAttachmentTableViewCell.collectionViewHeightDidUpdate
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let _ = self else { return }
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// composeStatusPollTableViewCell.collectionViewHeightDidUpdate
|
||||
// .receive(on: DispatchQueue.main)
|
||||
// .sink { [weak self] _ in
|
||||
// guard let _ = self else { return }
|
||||
// tableView.beginUpdates()
|
||||
// tableView.endUpdates()
|
||||
// }
|
||||
// .store(in: &disposeBag)
|
||||
|
||||
attachmentServices
|
||||
.removeDuplicates()
|
||||
|
@ -55,10 +73,10 @@ extension ComposeViewModel {
|
|||
let items = attachmentServices.map { ComposeStatusAttachmentItem.attachment(attachmentService: $0) }
|
||||
snapshot.appendItems(items, toSection: .main)
|
||||
|
||||
tableView.performBatchUpdates {
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
} completion: { _ in
|
||||
// do nothing
|
||||
if #available(iOS 15.0, *) {
|
||||
dataSource.applySnapshotUsingReloadData(snapshot)
|
||||
} else {
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -90,9 +108,11 @@ extension ComposeViewModel {
|
|||
snapshot.appendItems(items, toSection: .main)
|
||||
|
||||
tableView.performBatchUpdates {
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
} completion: { _ in
|
||||
// do nothing
|
||||
if #available(iOS 15.0, *) {
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
} else {
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
|
|
@ -35,6 +35,7 @@ final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
|||
collectionView.isScrollEnabled = false
|
||||
return collectionView
|
||||
}()
|
||||
let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
@ -68,6 +69,7 @@ extension ComposeStatusAttachmentTableViewCell {
|
|||
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
|
||||
guard let self = self else { return }
|
||||
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
|
||||
self.collectionViewHeightDidUpdate.send()
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
protocol ComposeStatusPollTableViewCellDelegate: AnyObject {
|
||||
func composeStatusPollTableViewCell(_ cell: ComposeStatusPollTableViewCell, pollOptionAttributesDidReorder options: [ComposeStatusPollItem.PollOptionAttribute])
|
||||
|
@ -49,6 +50,7 @@ final class ComposeStatusPollTableViewCell: UITableViewCell {
|
|||
collectionView.dragInteractionEnabled = true
|
||||
return collectionView
|
||||
}()
|
||||
let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
@ -82,6 +84,7 @@ extension ComposeStatusPollTableViewCell {
|
|||
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
|
||||
guard let self = self else { return }
|
||||
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
|
||||
self.collectionViewHeightDidUpdate.send()
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@ import Combine
|
|||
import MastodonSDK
|
||||
|
||||
protocol ComposeToolbarViewDelegate: AnyObject {
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: UIButton, mediaSelectionType type: ComposeToolbarView.MediaSelectionType)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: UIButton)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: UIButton)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: UIButton)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: UIButton, visibilitySelectionType type: ComposeToolbarView.VisibilitySelectionType)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: Any, mediaSelectionType type: ComposeToolbarView.MediaSelectionType)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: Any)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, emojiButtonDidPressed sender: Any)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: Any)
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: Any, visibilitySelectionType type: ComposeToolbarView.VisibilitySelectionType)
|
||||
}
|
||||
|
||||
final class ComposeToolbarView: UIView {
|
||||
|
@ -28,13 +28,9 @@ final class ComposeToolbarView: UIView {
|
|||
weak var delegate: ComposeToolbarViewDelegate?
|
||||
|
||||
// barButtonItem
|
||||
let mediaBarButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(UIImage(systemName: "photo"), for: .normal)
|
||||
return button
|
||||
}()
|
||||
private(set) lazy var mediaBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(customView: mediaBarButton)
|
||||
let barButtonItem = UIBarButtonItem()
|
||||
barButtonItem.image = UIImage(systemName: "photo")
|
||||
barButtonItem.accessibilityLabel = L10n.Scene.Compose.Accessibility.appendAttachment
|
||||
return barButtonItem
|
||||
}()
|
||||
|
@ -177,11 +173,17 @@ extension ComposeToolbarView {
|
|||
])
|
||||
characterCountLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
|
||||
mediaBarButtonItem.menu = createMediaContextMenu()
|
||||
mediaButton.menu = createMediaContextMenu()
|
||||
mediaButton.showsMenuAsPrimaryAction = true
|
||||
pollBarButtonItem.target = self
|
||||
pollBarButtonItem.action = #selector(ComposeToolbarView.pollButtonDidPressed(_:))
|
||||
pollButton.addTarget(self, action: #selector(ComposeToolbarView.pollButtonDidPressed(_:)), for: .touchUpInside)
|
||||
emojiButton.addTarget(self, action: #selector(ComposeToolbarView.emojiButtonDidPressed(_:)), for: .touchUpInside)
|
||||
contentWarningBarButtonItem.target = self
|
||||
contentWarningBarButtonItem.action = #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:))
|
||||
contentWarningButton.addTarget(self, action: #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:)), for: .touchUpInside)
|
||||
visibilityBarButtonItem.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
|
||||
visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
|
||||
visibilityButton.showsMenuAsPrimaryAction = true
|
||||
|
||||
|
@ -192,6 +194,7 @@ extension ComposeToolbarView {
|
|||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] type in
|
||||
guard let self = self else { return }
|
||||
self.visibilityBarButtonItem.menu = self.createVisibilityContextMenu(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
||||
self.visibilityButton.menu = self.createVisibilityContextMenu(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
@ -275,17 +278,22 @@ extension ComposeToolbarView {
|
|||
|
||||
switch traitCollection.userInterfaceStyle {
|
||||
case .light:
|
||||
mediaBarButtonItem.image = UIImage(systemName: "photo")
|
||||
mediaButton.setImage(UIImage(systemName: "photo", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
|
||||
contentWarningBarButtonItem.image = UIImage(systemName: "exclamationmark.shield")
|
||||
contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
|
||||
|
||||
case .dark:
|
||||
mediaBarButtonItem.image = UIImage(systemName: "photo.fill")
|
||||
mediaButton.setImage(UIImage(systemName: "photo.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
|
||||
contentWarningBarButtonItem.image = UIImage(systemName: "exclamationmark.shield.fill")
|
||||
contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
visibilityBarButtonItem.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
|
||||
visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
|
||||
}
|
||||
|
||||
|
@ -331,17 +339,17 @@ extension ComposeToolbarView {
|
|||
|
||||
extension ComposeToolbarView {
|
||||
|
||||
@objc private func pollButtonDidPressed(_ sender: UIButton) {
|
||||
@objc private func pollButtonDidPressed(_ sender: Any) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.composeToolbarView(self, pollButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
@objc private func emojiButtonDidPressed(_ sender: UIButton) {
|
||||
@objc private func emojiButtonDidPressed(_ sender: Any) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.composeToolbarView(self, emojiButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
@objc private func contentWarningButtonDidPressed(_ sender: UIButton) {
|
||||
@objc private func contentWarningButtonDidPressed(_ sender: Any) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.composeToolbarView(self, contentWarningButtonDidPressed: sender)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue