feat: update status content warning UI

This commit is contained in:
CMK 2022-02-08 19:50:18 +08:00
parent 9051e5d1ec
commit bdf7114fef
10 changed files with 224 additions and 76 deletions

View File

@ -140,7 +140,8 @@
"unreblog": "Undo reblog", "unreblog": "Undo reblog",
"favorite": "Favorite", "favorite": "Favorite",
"unfavorite": "Unfavorite", "unfavorite": "Unfavorite",
"menu": "Menu" "menu": "Menu",
"hide": "Hide"
}, },
"tag": { "tag": {
"url": "URL", "url": "URL",

View File

@ -6,9 +6,9 @@
// //
import UIKit import UIKit
import CoreDataStack
import MetaTextKit import MetaTextKit
import MastodonUI import MastodonUI
import CoreDataStack
// MARK: - header // MARK: - header
extension StatusTableViewCellDelegate where Self: DataSourceProvider { extension StatusTableViewCellDelegate where Self: DataSourceProvider {
@ -367,7 +367,29 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider {
func tableViewCell( func tableViewCell(
_ cell: UITableViewCell, _ cell: UITableViewCell,
statusView: StatusView, statusView: StatusView,
contentWarningToggleButtonDidPressed button: UIButton spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView
) {
Task {
let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
guard let item = await item(from: source) else {
assertionFailure()
return
}
guard case let .status(status) = item else {
assertionFailure("only works for status data provider")
return
}
try await DataSourceFacade.responseToToggleSensitiveAction(
dependency: self,
status: status
)
} // end Task
}
func tableViewCell(
_ cell: UITableViewCell,
statusView: StatusView,
spoilerBannerViewDidPressed bannerView: SpoilerBannerView
) { ) {
Task { Task {
let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil) let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)

View File

@ -37,9 +37,11 @@ extension SettingsAppearanceTableViewCell {
extension SettingsAppearanceTableViewCell.ViewModel { extension SettingsAppearanceTableViewCell.ViewModel {
func bind(cell: SettingsAppearanceTableViewCell) { func bind(cell: SettingsAppearanceTableViewCell) {
Publishers.CombineLatest( Publishers.CombineLatest(
$customUserInterfaceStyle, $customUserInterfaceStyle.removeDuplicates(),
$preferredTrueBlackDarkMode $preferredTrueBlackDarkMode.removeDuplicates()
) )
.debounce(for: 0.1, scheduler: DispatchQueue.main)
.receive(on: DispatchQueue.main)
.sink { customUserInterfaceStyle, preferredTrueBlackDarkMode in .sink { customUserInterfaceStyle, preferredTrueBlackDarkMode in
cell.appearanceViews.forEach { view in cell.appearanceViews.forEach { view in
view.selected = false view.selected = false

View File

@ -31,7 +31,8 @@ protocol StatusTableViewCellDelegate: AnyObject, AutoGenerateProtocolDelegate {
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, pollVoteButtonPressed button: UIButton) func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, pollVoteButtonPressed button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, actionToolbarContainer: ActionToolbarContainer, buttonDidPressed button: UIButton, action: ActionToolbarContainer.Action) func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, actionToolbarContainer: ActionToolbarContainer, buttonDidPressed button: UIButton, action: ActionToolbarContainer.Action)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action) func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, contentWarningToggleButtonDidPressed button: UIButton) func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, spoilerBannerViewDidPressed bannerView: SpoilerBannerView)
// sourcery:end // sourcery:end
} }
@ -72,8 +73,12 @@ extension StatusViewDelegate where Self: StatusViewContainerTableViewCell {
delegate?.tableViewCell(self, statusView: statusView, menuButton: button, didSelectAction: action) delegate?.tableViewCell(self, statusView: statusView, menuButton: button, didSelectAction: action)
} }
func statusView(_ statusView: StatusView, contentWarningToggleButtonDidPressed button: UIButton) { func statusView(_ statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView) {
delegate?.tableViewCell(self, statusView: statusView, contentWarningToggleButtonDidPressed: button) delegate?.tableViewCell(self, statusView: statusView, spoilerOverlayViewDidPressed: overlayView)
}
func statusView(_ statusView: StatusView, spoilerBannerViewDidPressed bannerView: SpoilerBannerView) {
delegate?.tableViewCell(self, statusView: statusView, spoilerBannerViewDidPressed: bannerView)
} }
// sourcery:end // sourcery:end
} }

View File

@ -15,7 +15,8 @@ extension MetaLabel {
case statusHeader case statusHeader
case statusName case statusName
case statusUsername case statusUsername
case statusSpoiler case statusSpoilerOverlay
case statusSpoilerBanner
case notificationTitle case notificationTitle
case profileFieldName case profileFieldName
case profileFieldValue case profileFieldValue
@ -57,11 +58,15 @@ extension MetaLabel {
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular)) font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
textColor = Asset.Colors.Label.secondary.color textColor = Asset.Colors.Label.secondary.color
case .statusSpoiler: case .statusSpoilerOverlay:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)) font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold))
textColor = Asset.Colors.Label.secondary.color textColor = Asset.Colors.Label.primary.color
textAlignment = .center textAlignment = .center
paragraphStyle.alignment = .center paragraphStyle.alignment = .center
case .statusSpoilerBanner:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
textColor = Asset.Colors.Label.primary.color
case .notificationTitle: case .notificationTitle:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 14, weight: .regular)) font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 14, weight: .regular))

View File

@ -380,7 +380,11 @@ extension NotificationView: StatusViewDelegate {
assertionFailure() assertionFailure()
} }
public func statusView(_ statusView: StatusView, contentWarningToggleButtonDidPressed button: UIButton) { public func statusView(_ statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView) {
assertionFailure()
}
public func statusView(_ statusView: StatusView, spoilerBannerViewDidPressed bannerView: SpoilerBannerView) {
assertionFailure() assertionFailure()
} }

View File

@ -256,8 +256,12 @@ extension StatusView.ViewModel {
.sink { spoilerContent, content, isContentReveal in .sink { spoilerContent, content, isContentReveal in
if let spoilerContent = spoilerContent { if let spoilerContent = spoilerContent {
statusView.spoilerOverlayView.spoilerMetaLabel.configure(content: spoilerContent) statusView.spoilerOverlayView.spoilerMetaLabel.configure(content: spoilerContent)
statusView.spoilerBannerView.label.configure(content: spoilerContent)
statusView.setSpoilerBannerViewHidden(isHidden: !isContentReveal)
} else { } else {
statusView.spoilerOverlayView.spoilerMetaLabel.reset() statusView.spoilerOverlayView.spoilerMetaLabel.reset()
statusView.spoilerBannerView.label.reset()
} }
if let content = content { if let content = content {
@ -273,7 +277,7 @@ extension StatusView.ViewModel {
statusView.contentMetaText.textView.accessibilityLabel = "" statusView.contentMetaText.textView.accessibilityLabel = ""
} }
statusView.setSpoilerOverlayViewHidden(isContentReveal) statusView.setSpoilerOverlayViewHidden(isHidden: isContentReveal)
} }
.store(in: &disposeBag) .store(in: &disposeBag)
// visibility // visibility
@ -299,16 +303,13 @@ extension StatusView.ViewModel {
} }
} }
.store(in: &disposeBag) .store(in: &disposeBag)
$isSensitive // $isSensitive
.sink { isSensitive in // .sink { isSensitive in
if isSensitive { // if isSensitive {
let image = Asset.Human.eyeCircleFill.image // statusView.setStatusSpoilerBannerViewDisplay()
statusView.contentWarningToggleButton.setImage(image, for: .normal) // }
statusView.contentWarningToggleButton.tintColor = .systemGray // }
statusView.setContentWarningToggleButtonDisplay() // .store(in: &disposeBag)
}
}
.store(in: &disposeBag)
// $spoilerContent // $spoilerContent
// .sink { metaContent in // .sink { metaContent in
// guard let metaContent = metaContent else { // guard let metaContent = metaContent else {

View File

@ -22,7 +22,8 @@ public protocol StatusViewDelegate: AnyObject {
func statusView(_ statusView: StatusView, pollVoteButtonPressed button: UIButton) func statusView(_ statusView: StatusView, pollVoteButtonPressed button: UIButton)
func statusView(_ statusView: StatusView, actionToolbarContainer: ActionToolbarContainer, buttonDidPressed button: UIButton, action: ActionToolbarContainer.Action) func statusView(_ statusView: StatusView, actionToolbarContainer: ActionToolbarContainer, buttonDidPressed button: UIButton, action: ActionToolbarContainer.Action)
func statusView(_ statusView: StatusView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action) func statusView(_ statusView: StatusView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action)
func statusView(_ statusView: StatusView, contentWarningToggleButtonDidPressed button: UIButton) func statusView(_ statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView)
func statusView(_ statusView: StatusView, spoilerBannerViewDidPressed bannerView: SpoilerBannerView)
// func statusView(_ statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) // func statusView(_ statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
// func statusView(_ statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) // func statusView(_ statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
} }
@ -100,9 +101,6 @@ public final class StatusView: UIView {
return button return button
}() }()
// contentWarningToggleButton
public let contentWarningToggleButton = UIButton(type: .system)
// content // content
let contentContainer = UIStackView() let contentContainer = UIStackView()
public let contentMetaText: MetaText = { public let contentMetaText: MetaText = {
@ -134,6 +132,7 @@ public final class StatusView: UIView {
return metaText return metaText
}() }()
// content warning
let spoilerOverlayView = SpoilerOverlayView() let spoilerOverlayView = SpoilerOverlayView()
// media // media
@ -197,6 +196,9 @@ public final class StatusView: UIView {
// visibility // visibility
public let statusVisibilityView = StatusVisibilityView() public let statusVisibilityView = StatusVisibilityView()
// spoiler banner
public let spoilerBannerView = SpoilerBannerView()
// toolbar // toolbar
public let actionToolbarContainer = ActionToolbarContainer() public let actionToolbarContainer = ActionToolbarContainer()
@ -222,11 +224,11 @@ public final class StatusView: UIView {
} }
headerContainerView.isHidden = true headerContainerView.isHidden = true
contentWarningToggleButton.isHidden = true setSpoilerOverlayViewHidden(isHidden: true)
setSpoilerOverlayViewHidden(true)
mediaContainerView.isHidden = true mediaContainerView.isHidden = true
pollContainerView.isHidden = true pollContainerView.isHidden = true
statusVisibilityView.isHidden = true statusVisibilityView.isHidden = true
setSpoilerBannerViewHidden(isHidden: true)
} }
public override init(frame: CGRect) { public override init(frame: CGRect) {
@ -265,12 +267,15 @@ extension StatusView {
authorNameLabel.isUserInteractionEnabled = false authorNameLabel.isUserInteractionEnabled = false
authorUsernameLabel.isUserInteractionEnabled = false authorUsernameLabel.isUserInteractionEnabled = false
// contentWarningToggleButton
contentWarningToggleButton.addTarget(self, action: #selector(StatusView.contentWarningToggleButtonDidPressed(_:)), for: .touchUpInside)
// dateLabel // dateLabel
dateLabel.isUserInteractionEnabled = false dateLabel.isUserInteractionEnabled = false
// content warning
let spoilerOverlayViewTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
spoilerOverlayView.addGestureRecognizer(spoilerOverlayViewTapGestureRecognizer)
spoilerOverlayViewTapGestureRecognizer.addTarget(self, action: #selector(StatusView.spoilerOverlayViewTapGestureRecognizerHandler(_:)))
// content // content
contentMetaText.textView.delegate = self contentMetaText.textView.delegate = self
contentMetaText.textView.linkDelegate = self contentMetaText.textView.linkDelegate = self
@ -287,6 +292,11 @@ extension StatusView {
pollTableView.delegate = self pollTableView.delegate = self
pollVoteButton.addTarget(self, action: #selector(StatusView.pollVoteButtonDidPressed(_:)), for: .touchUpInside) pollVoteButton.addTarget(self, action: #selector(StatusView.pollVoteButtonDidPressed(_:)), for: .touchUpInside)
// statusSpoilerBannerView
let spoilerBannerViewTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
spoilerBannerView.addGestureRecognizer(spoilerBannerViewTapGestureRecognizer)
spoilerBannerViewTapGestureRecognizer.addTarget(self, action: #selector(StatusView.spoilerBannerViewTapGestureRecognizerHandler(_:)))
// toolbar // toolbar
actionToolbarContainer.delegate = self actionToolbarContainer.delegate = self
} }
@ -305,16 +315,22 @@ extension StatusView {
delegate?.statusView(self, authorAvatarButtonDidPressed: avatarButton) delegate?.statusView(self, authorAvatarButtonDidPressed: avatarButton)
} }
@objc private func contentWarningToggleButtonDidPressed(_ sender: UIButton) {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
delegate?.statusView(self, contentWarningToggleButtonDidPressed: contentWarningToggleButton)
}
@objc private func pollVoteButtonDidPressed(_ sender: UIButton) { @objc private func pollVoteButtonDidPressed(_ sender: UIButton) {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)") logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
delegate?.statusView(self, pollVoteButtonPressed: pollVoteButton) delegate?.statusView(self, pollVoteButtonPressed: pollVoteButton)
} }
@objc private func spoilerOverlayViewTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
delegate?.statusView(self, spoilerOverlayViewDidPressed: spoilerOverlayView)
}
@objc private func spoilerBannerViewTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
delegate?.statusView(self, spoilerBannerViewDidPressed: spoilerBannerView)
}
} }
extension StatusView { extension StatusView {
@ -354,7 +370,7 @@ extension StatusView.Style {
} }
} }
func inline(statusView: StatusView) { private func base(statusView: StatusView) {
// container: V - [ header container | author container | content container | media container | pollTableView | actionToolbarContainer ] // container: V - [ header container | author container | content container | media container | pollTableView | actionToolbarContainer ]
statusView.containerStackView.layoutMargins = StatusView.containerLayoutMargin statusView.containerStackView.layoutMargins = StatusView.containerLayoutMargin
@ -440,11 +456,6 @@ extension StatusView.Style {
statusView.dateLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal) statusView.dateLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
authorSecondaryMetaContainer.addArrangedSubview(UIView()) authorSecondaryMetaContainer.addArrangedSubview(UIView())
// contentWarningToggleButton
statusView.authorContainerView.addArrangedSubview(statusView.contentWarningToggleButton)
statusView.contentWarningToggleButton.setContentHuggingPriority(.required - 2, for: .horizontal)
statusView.contentWarningToggleButton.setContentCompressionResistancePriority(.required - 2, for: .horizontal)
// content container: V - [ contentMetaText ] // content container: V - [ contentMetaText ]
statusView.contentContainer.axis = .vertical statusView.contentContainer.axis = .vertical
statusView.contentContainer.spacing = 12 statusView.contentContainer.spacing = 12
@ -508,15 +519,24 @@ extension StatusView.Style {
statusView.statusVisibilityView.preservesSuperviewLayoutMargins = true statusView.statusVisibilityView.preservesSuperviewLayoutMargins = true
statusView.containerStackView.addArrangedSubview(statusView.statusVisibilityView) statusView.containerStackView.addArrangedSubview(statusView.statusVisibilityView)
statusView.spoilerBannerView.preservesSuperviewLayoutMargins = true
statusView.containerStackView.addArrangedSubview(statusView.spoilerBannerView)
// action toolbar // action toolbar
statusView.actionToolbarContainer.configure(for: .inline) statusView.actionToolbarContainer.configure(for: .inline)
statusView.actionToolbarContainer.preservesSuperviewLayoutMargins = true statusView.actionToolbarContainer.preservesSuperviewLayoutMargins = true
statusView.containerStackView.addArrangedSubview(statusView.actionToolbarContainer) statusView.containerStackView.addArrangedSubview(statusView.actionToolbarContainer)
} }
func inline(statusView: StatusView) {
base(statusView: statusView)
statusView.statusVisibilityView.removeFromSuperview()
}
func plain(statusView: StatusView) { func plain(statusView: StatusView) {
// container: V - [ | statusMetricView ] // container: V - [ | statusMetricView ]
inline(statusView: statusView) // override the inline style base(statusView: statusView) // override the base style
// statusMetricView // statusMetricView
statusView.statusMetricView.layoutMargins = StatusView.containerLayoutMargin statusView.statusMetricView.layoutMargins = StatusView.containerLayoutMargin
@ -530,7 +550,7 @@ extension StatusView.Style {
} }
func report(statusView: StatusView) { func report(statusView: StatusView) {
inline(statusView: statusView) // override the inline style base(statusView: statusView) // override the base style
statusView.menuButton.removeFromSuperview() statusView.menuButton.removeFromSuperview()
statusView.statusVisibilityView.removeFromSuperview() statusView.statusVisibilityView.removeFromSuperview()
@ -538,31 +558,36 @@ extension StatusView.Style {
} }
func notification(statusView: StatusView) { func notification(statusView: StatusView) {
inline(statusView: statusView) // override the inline style base(statusView: statusView) // override the base style
statusView.headerContainerView.removeFromSuperview() statusView.headerContainerView.removeFromSuperview()
statusView.authorContainerView.removeFromSuperview() statusView.authorContainerView.removeFromSuperview()
statusView.statusVisibilityView.removeFromSuperview()
statusView.spoilerBannerView.removeFromSuperview()
} }
func notificationQuote(statusView: StatusView) { func notificationQuote(statusView: StatusView) {
inline(statusView: statusView) // override the inline style base(statusView: statusView) // override the base style
statusView.contentContainer.layoutMargins.bottom = 16 // fix contentText align to edge issue statusView.contentContainer.layoutMargins.bottom = 16 // fix contentText align to edge issue
statusView.menuButton.removeFromSuperview() statusView.menuButton.removeFromSuperview()
statusView.statusVisibilityView.removeFromSuperview() statusView.statusVisibilityView.removeFromSuperview()
statusView.spoilerBannerView.removeFromSuperview()
statusView.actionToolbarContainer.removeFromSuperview() statusView.actionToolbarContainer.removeFromSuperview()
} }
func composeStatusReplica(statusView: StatusView) { func composeStatusReplica(statusView: StatusView) {
inline(statusView: statusView) base(statusView: statusView)
statusView.avatarButton.isUserInteractionEnabled = false statusView.avatarButton.isUserInteractionEnabled = false
statusView.menuButton.removeFromSuperview() statusView.menuButton.removeFromSuperview()
statusView.statusVisibilityView.removeFromSuperview()
statusView.spoilerBannerView.removeFromSuperview()
statusView.actionToolbarContainer.removeFromSuperview() statusView.actionToolbarContainer.removeFromSuperview()
} }
func composeStatusAuthor(statusView: StatusView) { func composeStatusAuthor(statusView: StatusView) {
inline(statusView: statusView) base(statusView: statusView)
statusView.avatarButton.isUserInteractionEnabled = false statusView.avatarButton.isUserInteractionEnabled = false
statusView.menuButton.removeFromSuperview() statusView.menuButton.removeFromSuperview()
@ -573,6 +598,7 @@ extension StatusView.Style {
statusView.mediaContainerView.removeFromSuperview() statusView.mediaContainerView.removeFromSuperview()
statusView.pollContainerView.removeFromSuperview() statusView.pollContainerView.removeFromSuperview()
statusView.statusVisibilityView.removeFromSuperview() statusView.statusVisibilityView.removeFromSuperview()
statusView.spoilerBannerView.removeFromSuperview()
statusView.actionToolbarContainer.removeFromSuperview() statusView.actionToolbarContainer.removeFromSuperview()
} }
@ -583,11 +609,7 @@ extension StatusView {
headerContainerView.isHidden = false headerContainerView.isHidden = false
} }
func setContentWarningToggleButtonDisplay() { func setSpoilerOverlayViewHidden(isHidden: Bool) {
contentWarningToggleButton.isHidden = false
}
func setSpoilerOverlayViewHidden(_ isHidden: Bool) {
spoilerOverlayView.isHidden = isHidden spoilerOverlayView.isHidden = isHidden
spoilerOverlayView.setComponentHidden(isHidden) spoilerOverlayView.setComponentHidden(isHidden)
} }
@ -604,6 +626,10 @@ extension StatusView {
statusVisibilityView.isHidden = false statusVisibilityView.isHidden = false
} }
func setSpoilerBannerViewHidden(isHidden: Bool) {
spoilerBannerView.isHidden = isHidden
}
// content text Width // content text Width
public var contentMaxLayoutWidth: CGFloat { public var contentMaxLayoutWidth: CGFloat {
let inset = contentLayoutInset let inset = contentLayoutInset

View File

@ -0,0 +1,94 @@
//
// SpoilerBannerView.swift
//
//
// Created by MainasuK on 2022-2-8.
//
import UIKit
import MastodonAsset
import MetaTextKit
public final class SpoilerBannerView: UIView {
static let cornerRadius: CGFloat = 8
static let containerMargin: CGFloat = 14
public let containerView = UIView()
public let label = MetaLabel(style: .statusSpoilerBanner)
public let hideLabel: UILabel = {
let label = UILabel()
label.textColor = Asset.Colors.Label.primary.color
label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
label.numberOfLines = 0
label.text = "Hide" // TODO: i18n
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
}
extension SpoilerBannerView {
private func _init() {
containerView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerView)
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor),
containerView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
containerView.backgroundColor = .secondarySystemBackground
containerView.layoutMargins = UIEdgeInsets(
top: StatusVisibilityView.containerMargin,
left: StatusVisibilityView.containerMargin,
bottom: StatusVisibilityView.containerMargin,
right: StatusVisibilityView.containerMargin
)
let labelContainer = UIStackView()
labelContainer.axis = .horizontal
labelContainer.spacing = 16
labelContainer.alignment = .center
labelContainer.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(labelContainer)
NSLayoutConstraint.activate([
labelContainer.topAnchor.constraint(equalTo: containerView.layoutMarginsGuide.topAnchor),
labelContainer.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor),
labelContainer.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor),
labelContainer.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor),
])
labelContainer.addArrangedSubview(label)
labelContainer.addArrangedSubview(hideLabel)
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
hideLabel.setContentHuggingPriority(.required - 1, for: .horizontal)
hideLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
label.isUserInteractionEnabled = false
}
public override func layoutSubviews() {
super.layoutSubviews()
containerView.layer.masksToBounds = false
containerView.layer.cornerCurve = .continuous
containerView.layer.cornerRadius = StatusVisibilityView.cornerRadius
}
}

View File

@ -10,33 +10,27 @@ import MastodonLocalization
import MastodonAsset import MastodonAsset
import MetaTextKit import MetaTextKit
final class SpoilerOverlayView: UIView { public final class SpoilerOverlayView: UIView {
let containerStackView: UIStackView = { let containerStackView: UIStackView = {
let stackView = UIStackView() let stackView = UIStackView()
stackView.axis = .vertical stackView.axis = .vertical
// stackView.spacing = 8 stackView.spacing = 8
stackView.alignment = .center stackView.alignment = .center
return stackView return stackView
}() }()
let iconImageView: UIImageView = { let spoilerMetaLabel = MetaLabel(style: .statusSpoilerOverlay)
let imageView = UIImageView()
imageView.image = UIImage(systemName: "eye", withConfiguration: UIImage.SymbolConfiguration(font: .systemFont(ofSize: 34, weight: .light))) let hintLabel: UILabel = {
imageView.tintColor = Asset.Colors.Label.secondary.color
return imageView
}()
let titleLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold)) label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
label.textAlignment = .center label.textAlignment = .center
label.textColor = Asset.Colors.Label.primary.color label.textColor = Asset.Colors.Label.secondary.color
label.text = L10n.Common.Controls.Status.contentWarning label.text = L10n.Common.Controls.Status.mediaContentWarning
return label return label
}() }()
let spoilerMetaLabel = MetaLabel(style: .statusSpoiler)
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -61,19 +55,11 @@ extension SpoilerOverlayView {
containerStackView.bottomAnchor.constraint(equalTo: bottomAnchor), containerStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
]) ])
let topPaddingView = UIView() let topPaddingView = UIView()
topPaddingView.translatesAutoresizingMaskIntoConstraints = false topPaddingView.translatesAutoresizingMaskIntoConstraints = false
containerStackView.addArrangedSubview(topPaddingView) containerStackView.addArrangedSubview(topPaddingView)
iconImageView.translatesAutoresizingMaskIntoConstraints = false
containerStackView.addArrangedSubview(iconImageView)
NSLayoutConstraint.activate([
iconImageView.widthAnchor.constraint(equalToConstant: 52.0).priority(.required - 1),
iconImageView.heightAnchor.constraint(equalToConstant: 32.0).priority(.required - 1),
])
iconImageView.setContentCompressionResistancePriority(.required, for: .vertical)
containerStackView.addArrangedSubview(titleLabel)
containerStackView.addArrangedSubview(spoilerMetaLabel) containerStackView.addArrangedSubview(spoilerMetaLabel)
containerStackView.addArrangedSubview(hintLabel)
let bottomPaddingView = UIView() let bottomPaddingView = UIView()
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
containerStackView.addArrangedSubview(bottomPaddingView) containerStackView.addArrangedSubview(bottomPaddingView)
@ -82,6 +68,8 @@ extension SpoilerOverlayView {
]) ])
topPaddingView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical) topPaddingView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical)
bottomPaddingView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical) bottomPaddingView.setContentCompressionResistancePriority(.defaultLow - 100, for: .vertical)
spoilerMetaLabel.isUserInteractionEnabled = false
} }
public func setComponentHidden(_ isHidden: Bool) { public func setComponentHidden(_ isHidden: Bool) {