// // ComposeStatusContentTableViewCell.swift // Mastodon // // Created by MainasuK Cirno on 2021-6-28. // import os.log import UIKit import Combine import MetaTextView import UITextView_Placeholder final class ComposeStatusContentTableViewCell: UITableViewCell { let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "UI") var disposeBag = Set() let statusView = ReplicaStatusView() let statusContentWarningEditorView = StatusContentWarningEditorView() let textEditorViewContainerView = UIView() static let metaTextViewTag: Int = 333 let metaText: MetaText = { let metaText = MetaText() metaText.textView.backgroundColor = .clear metaText.textView.isScrollEnabled = false metaText.textView.keyboardType = .twitter metaText.textView.textDragInteraction?.isEnabled = false // disable drag for link and attachment metaText.textView.textContainer.lineFragmentPadding = 10 // leading inset metaText.textView.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) metaText.textView.attributedPlaceholder = { var attributes = metaText.textAttributes attributes[.foregroundColor] = Asset.Colors.Label.secondary.color return NSAttributedString( string: L10n.Scene.Compose.contentInputPlaceholder, attributes: attributes ) }() let paragraphStyle: NSMutableParagraphStyle = { let style = NSMutableParagraphStyle() style.lineSpacing = 5 return style }() metaText.textAttributes = [ .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)), .foregroundColor: Asset.Colors.Label.primary.color, .paragraphStyle: paragraphStyle, ] metaText.linkAttributes = [ .font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)), .foregroundColor: Asset.Colors.brandBlue.color, .paragraphStyle: paragraphStyle, ] return metaText }() // output let contentWarningContent = PassthroughSubject() override func prepareForReuse() { super.prepareForReuse() metaText.delegate = nil metaText.textView.delegate = nil } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) _init() } required init?(coder: NSCoder) { super.init(coder: coder) _init() } } extension ComposeStatusContentTableViewCell { private func _init() { selectionStyle = .none layer.zPosition = 999 backgroundColor = .clear preservesSuperviewLayoutMargins = true let containerStackView = UIStackView() containerStackView.axis = .vertical containerStackView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(containerStackView) NSLayoutConstraint.activate([ containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor), containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), ]) containerStackView.preservesSuperviewLayoutMargins = true containerStackView.addArrangedSubview(statusContentWarningEditorView) statusContentWarningEditorView.setContentHuggingPriority(.required - 1, for: .vertical) let statusContainerView = UIView() statusContainerView.preservesSuperviewLayoutMargins = true containerStackView.addArrangedSubview(statusContainerView) statusView.translatesAutoresizingMaskIntoConstraints = false statusContainerView.addSubview(statusView) NSLayoutConstraint.activate([ statusView.topAnchor.constraint(equalTo: statusContainerView.topAnchor, constant: 20), statusView.leadingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.leadingAnchor), statusView.trailingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.trailingAnchor), statusView.bottomAnchor.constraint(equalTo: statusContainerView.bottomAnchor), ]) containerStackView.addArrangedSubview(textEditorViewContainerView) metaText.textView.translatesAutoresizingMaskIntoConstraints = false textEditorViewContainerView.addSubview(metaText.textView) NSLayoutConstraint.activate([ metaText.textView.topAnchor.constraint(equalTo: textEditorViewContainerView.topAnchor), metaText.textView.leadingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.leadingAnchor), metaText.textView.trailingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.trailingAnchor), metaText.textView.bottomAnchor.constraint(equalTo: textEditorViewContainerView.bottomAnchor), metaText.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 88).priority(.defaultHigh), ]) statusContentWarningEditorView.textView.delegate = self statusView.nameTrialingDotLabel.isHidden = true statusView.dateLabel.isHidden = true statusContentWarningEditorView.isHidden = true statusView.statusContainerStackView.isHidden = true } } // MARK: - UITextViewDelegate extension ComposeStatusContentTableViewCell: UITextViewDelegate { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { switch textView { case statusContentWarningEditorView.textView: // disable input line break guard text != "\n" else { return false } return true default: assertionFailure() return true } } func textViewDidChange(_ textView: UITextView) { logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): text: \(textView.text ?? "")") guard textView === statusContentWarningEditorView.textView else { return } // replace line break with space textView.text = textView.text.replacingOccurrences(of: "\n", with: " ") contentWarningContent.send(textView.text) } }