diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index eaf9efc5..c235224d 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -482,7 +482,6 @@ DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5527B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift */; }; DBB45B5927B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */; }; DBB45B5B27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */; }; - DBB45B5E27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */; }; DBB45B6027B50A4F002DC5A7 /* RecommendAccountItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B5F27B50A4F002DC5A7 /* RecommendAccountItem.swift */; }; DBB45B6227B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */; }; DBB525082611EAC0002F1F29 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = DBB525072611EAC0002F1F29 /* Tabman */; }; @@ -1230,7 +1229,6 @@ DBB45B5527B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewVideoViewController.swift; sourceTree = ""; }; DBB45B5827B39FE4002DC5A7 /* MediaPreviewVideoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewVideoViewModel.swift; sourceTree = ""; }; DBB45B5A27B3A109002DC5A7 /* MediaPreviewTransitionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewTransitionViewController.swift; sourceTree = ""; }; - DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveMarginStatusTableViewCell.swift; sourceTree = ""; }; DBB45B5F27B50A4F002DC5A7 /* RecommendAccountItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountItem.swift; sourceTree = ""; }; DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SuggestionAccountViewModel+Diffable.swift"; sourceTree = ""; }; DBB5250D2611EBAF002F1F29 /* ProfileSegmentedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSegmentedViewController.swift; sourceTree = ""; }; @@ -1745,7 +1743,6 @@ DBE3CDBA261C427900430CC6 /* TimelineHeaderTableViewCell.swift */, DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */, DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */, - DBB45B5D27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift */, ); path = TableviewCell; sourceTree = ""; @@ -3841,7 +3838,6 @@ DBA465952696E387002B41DB /* AppPreference.swift in Sources */, 2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */, DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */, - DBB45B5E27B4EB22002DC5A7 /* AdaptiveMarginStatusTableViewCell.swift in Sources */, DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */, DBA5E7A3263AD0A3004598BB /* PhotoLibraryService.swift in Sources */, DBD5B1F627BCD3D200BD6B38 /* SuggestionAccountTableViewCell+ViewModel.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index e854e877..643aa9f6 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ AppShared.xcscheme_^#shared#^_ orderHint - 29 + 19 CoreDataStack.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 28 + 20 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 30 + 18 SuppressBuildableAutocreation diff --git a/Mastodon/Scene/Notification/Cell/NotificationTableViewCell+ViewModel.swift b/Mastodon/Scene/Notification/Cell/NotificationTableViewCell+ViewModel.swift index be230391..2a4eb8a9 100644 --- a/Mastodon/Scene/Notification/Cell/NotificationTableViewCell+ViewModel.swift +++ b/Mastodon/Scene/Notification/Cell/NotificationTableViewCell+ViewModel.swift @@ -36,7 +36,7 @@ extension NotificationTableViewCell { logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): did layout for new cell") notificationView.statusView.frame.size.width = tableView.frame.width - containerViewHorizontalMargin - notificationView.quoteStatusView.frame.size.width = tableView.frame.width - StatusView.containerLayoutMargin.left - StatusView.containerLayoutMargin.right - containerViewHorizontalMargin + notificationView.quoteStatusView.frame.size.width = tableView.frame.width - 2 * StatusView.containerLayoutMargin - containerViewHorizontalMargin } switch viewModel.value { diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift index d6bbd507..12925ca4 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift @@ -26,9 +26,6 @@ final class UserTimelineViewController: UIViewController, NeedsDependency, Media lazy var tableView: UITableView = { let tableView = UITableView() - tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self)) - tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) - tableView.register(TimelineHeaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineHeaderTableViewCell.self)) tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 tableView.separatorStyle = .none diff --git a/Mastodon/Scene/Share/View/TableviewCell/AdaptiveMarginStatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/AdaptiveMarginStatusTableViewCell.swift deleted file mode 100644 index 78363d09..00000000 --- a/Mastodon/Scene/Share/View/TableviewCell/AdaptiveMarginStatusTableViewCell.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// AdaptiveMarginStatusTableViewCell.swift -// Mastodon -// -// Created by MainasuK on 2022-2-10. -// - -import UIKit -import MastodonUI - -protocol AdaptiveContainerMarginTableViewCell: UITableViewCell { - associatedtype ContainerView: UIView - static var containerViewMarginForRegularHorizontalSizeClass: CGFloat { get } - var containerView: ContainerView { get } - var containerViewLeadingLayoutConstraint: NSLayoutConstraint! { get set } - var containerViewTrailingLayoutConstraint: NSLayoutConstraint! { get set } -} - -extension AdaptiveContainerMarginTableViewCell { - - static var containerViewMarginForRegularHorizontalSizeClass: CGFloat { 64 } - - func setupContainerViewMarginConstraints() { - containerViewLeadingLayoutConstraint = containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) - containerViewTrailingLayoutConstraint = contentView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor) - } - - func updateContainerViewMarginConstraints() { - guard traitCollection.userInterfaceIdiom != .phone, - traitCollection.horizontalSizeClass == .regular - else { - containerViewLeadingLayoutConstraint.constant = 0 - containerViewTrailingLayoutConstraint.constant = 0 - return - } - - containerViewLeadingLayoutConstraint.constant = Self.containerViewMarginForRegularHorizontalSizeClass - containerViewTrailingLayoutConstraint.constant = Self.containerViewMarginForRegularHorizontalSizeClass - } - - var containerViewHorizontalMargin: CGFloat { - containerViewLeadingLayoutConstraint.constant + containerViewTrailingLayoutConstraint.constant - } - -} diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift index 3ec9aa90..a1033f05 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift @@ -28,20 +28,6 @@ final class StatusTableViewCell: UITableViewCell { var containerViewLeadingLayoutConstraint: NSLayoutConstraint! var containerViewTrailingLayoutConstraint: NSLayoutConstraint! -// var isFiltered: Bool = false { -// didSet { -// configure(isFiltered: isFiltered) -// } -// } -// -// let filteredLabel: UILabel = { -// let label = UILabel() -// label.textColor = Asset.Colors.Label.secondary.color -// label.text = L10n.Common.Controls.Timeline.filtered -// label.font = .preferredFont(forTextStyle: .body) -// return label -// }() -// override func prepareForReuse() { super.prepareForReuse() @@ -71,7 +57,6 @@ extension StatusTableViewCell { statusView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(statusView) setupContainerViewMarginConstraints() - updateContainerViewMarginConstraints() NSLayoutConstraint.activate([ statusView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16), containerViewLeadingLayoutConstraint, @@ -79,6 +64,7 @@ extension StatusTableViewCell { statusView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), ]) statusView.setup(style: .inline) + updateContainerViewMarginConstraints() separatorLine.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(separatorLine) diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift index d971cada..e27cc2dd 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift @@ -58,7 +58,6 @@ extension StatusThreadRootTableViewCell { statusView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(statusView) setupContainerViewMarginConstraints() - updateContainerViewMarginConstraints() NSLayoutConstraint.activate([ statusView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16), containerViewLeadingLayoutConstraint, @@ -66,6 +65,7 @@ extension StatusThreadRootTableViewCell { statusView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), ]) statusView.setup(style: .plain) + updateContainerViewMarginConstraints() separatorLine.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(separatorLine) @@ -108,7 +108,6 @@ extension StatusThreadRootTableViewCell { statusView.mediaGridContainerView, statusView.pollTableView, statusView.pollStatusStackView, - statusView.statusVisibilityView, statusView.actionToolbarContainer, statusView.statusMetricView ] @@ -123,10 +122,6 @@ extension StatusThreadRootTableViewCell { elements.removeAll(where: { $0 === statusView.contentMetaText.textView }) } - if statusView.statusVisibilityView.isHidden { - elements.removeAll(where: { $0 === statusView.statusVisibilityView }) - } - if statusView.viewModel.pollItems.isEmpty { elements.removeAll(where: { $0 === statusView.pollTableView }) elements.removeAll(where: { $0 === statusView.pollStatusStackView }) diff --git a/MastodonSDK/Sources/MastodonUI/Protocol/AdaptiveMarginStatusTableViewCell.swift b/MastodonSDK/Sources/MastodonUI/Protocol/AdaptiveMarginStatusTableViewCell.swift new file mode 100644 index 00000000..b552816b --- /dev/null +++ b/MastodonSDK/Sources/MastodonUI/Protocol/AdaptiveMarginStatusTableViewCell.swift @@ -0,0 +1,56 @@ +// +// AdaptiveMarginStatusTableViewCell.swift +// +// +// Created by MainasuK on 2022-2-18. +// + +import UIKit + +public protocol AdaptiveContainerView: UIView { + func updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: Bool) +} + +public protocol AdaptiveContainerMarginTableViewCell: UITableViewCell { + associatedtype ContainerView: AdaptiveContainerView + static var containerViewMarginForRegularHorizontalSizeClass: CGFloat { get } + var containerView: ContainerView { get } + var containerViewLeadingLayoutConstraint: NSLayoutConstraint! { get set } + var containerViewTrailingLayoutConstraint: NSLayoutConstraint! { get set } +} + +extension AdaptiveContainerMarginTableViewCell { + + public static var containerViewMarginForRegularHorizontalSizeClass: CGFloat { 64 } + + public func setupContainerViewMarginConstraints() { + containerViewLeadingLayoutConstraint = containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) + containerViewTrailingLayoutConstraint = contentView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor) + } + + public func updateContainerViewMarginConstraints() { + func setupContainerForPhone() { + containerView.updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: true) // add inner margin for phone + containerViewLeadingLayoutConstraint.constant = 0 // remove outer margin for phone + containerViewTrailingLayoutConstraint.constant = 0 + } + + switch traitCollection.userInterfaceIdiom { + case .phone: + setupContainerForPhone() + default: + guard traitCollection.horizontalSizeClass == .regular else { + setupContainerForPhone() + return + } + containerView.updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: false) // remove margin for iPad + containerViewLeadingLayoutConstraint.constant = Self.containerViewMarginForRegularHorizontalSizeClass // add outer margin for iPad + containerViewTrailingLayoutConstraint.constant = Self.containerViewMarginForRegularHorizontalSizeClass + } + } + + public var containerViewHorizontalMargin: CGFloat { + containerViewLeadingLayoutConstraint.constant + containerViewTrailingLayoutConstraint.constant + } + +} diff --git a/MastodonSDK/Sources/MastodonUI/View/Container/AdaptiveMarginContainerView.swift b/MastodonSDK/Sources/MastodonUI/View/Container/AdaptiveMarginContainerView.swift new file mode 100644 index 00000000..467715d7 --- /dev/null +++ b/MastodonSDK/Sources/MastodonUI/View/Container/AdaptiveMarginContainerView.swift @@ -0,0 +1,55 @@ +// +// AdaptiveMarginContainerView.swift +// +// +// Created by MainasuK on 2022-2-18. +// + +import UIKit + +public final class AdaptiveMarginContainerView: UIView { + + public var margin: CGFloat = 0 { + didSet { updateConstraints() } + } + + public var contentView: UIView? { + didSet { + guard let contentView = contentView else { return } + guard contentView.superview == nil else { return } + + contentView.translatesAutoresizingMaskIntoConstraints = false + addSubview(contentView) + + let _leadingLayoutConstraint = contentView.leadingAnchor.constraint(equalTo: leadingAnchor) + let _trailingLayoutConstraint = trailingAnchor.constraint(equalTo: contentView.trailingAnchor) + + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(equalTo: topAnchor), + _leadingLayoutConstraint, + _trailingLayoutConstraint, + contentView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + + leadingLayoutConstraint = _leadingLayoutConstraint + trailingLayoutConstraint = _trailingLayoutConstraint + + updateConstraints() + } + } + + var leadingLayoutConstraint: NSLayoutConstraint? + var trailingLayoutConstraint: NSLayoutConstraint? + +} + +extension AdaptiveMarginContainerView { + + public override func updateConstraints() { + super.updateConstraints() + + leadingLayoutConstraint?.constant = margin + trailingLayoutConstraint?.constant = margin + } + +} diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift index 1d988e45..f4cee092 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift @@ -22,6 +22,7 @@ public final class MediaView: UIView { formatter.allowedUnits = [.minute, .second] return formatter }() + public static let placeholderImage = UIImage.placeholder(color: .systemGray6) public let container = TouchBlockingView() @@ -154,7 +155,10 @@ extension MediaView { .receive(on: DispatchQueue.main) .sink { [weak self] isReveal, previewImage, blurhashImage in guard let self = self else { return } - let image = isReveal ? (previewImage ?? blurhashImage) : blurhashImage + + let image = isReveal ? + (previewImage ?? blurhashImage ?? MediaView.placeholderImage) : + (blurhashImage ?? MediaView.placeholderImage) self.imageView.image = image } .store(in: &configuration.disposeBag) @@ -204,30 +208,6 @@ extension MediaView { assetURL: info.previewURL ) bindImage(configuration: configuration, info: imageInfo) - -// indicatorBlurEffectView.translatesAutoresizingMaskIntoConstraints = false -// imageView.addSubview(indicatorBlurEffectView) -// NSLayoutConstraint.activate([ -// imageView.trailingAnchor.constraint(equalTo: indicatorBlurEffectView.trailingAnchor, constant: 11), -// imageView.bottomAnchor.constraint(equalTo: indicatorBlurEffectView.bottomAnchor, constant: 8), -// ]) -// setupIndicatorViewHierarchy() - -// playerIndicatorLabel.attributedText = { -// let imageAttachment = NSTextAttachment(image: UIImage(systemName: "play.fill")!) -// let imageAttributedString = AttributedString(NSAttributedString(attachment: imageAttachment)) -// let duration: String = { -// guard let durationMS = info.durationMS else { return "" } -// let timeInterval = TimeInterval(durationMS / 1000) -// guard timeInterval > 0 else { return "" } -// guard let text = MediaView.durationFormatter.string(from: timeInterval) else { return "" } -// return " \(text)" -// }() -// let textAttributedString = AttributedString("\(duration)") -// var attributedString = imageAttributedString + textAttributedString -// attributedString.foregroundColor = .secondaryLabel -// return NSAttributedString(attributedString) -// }() } private func layoutBlurhash() { diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/NotificationView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/NotificationView.swift index 4c5e8aba..a8b7f469 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/NotificationView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/NotificationView.swift @@ -136,7 +136,7 @@ public final class NotificationView: UIView { extension NotificationView { private func _init() { // container: V - [ author container | (authorContainerViewBottomPaddingView) | statusView | quoteStatusView ] - containerStackView.layoutMargins = StatusView.containerLayoutMargin + // containerStackView.layoutMargins = StatusView.containerLayoutMargin containerStackView.translatesAutoresizingMaskIntoConstraints = false addSubview(containerStackView) @@ -228,9 +228,9 @@ extension NotificationView { containerStackView.addArrangedSubview(quoteStatusViewContainerView) quoteStatusViewContainerView.layoutMargins = UIEdgeInsets( top: 0, - left: StatusView.containerLayoutMargin.left, + left: StatusView.containerLayoutMargin, bottom: 16, - right: StatusView.containerLayoutMargin.right + right: StatusView.containerLayoutMargin ) quoteBackgroundView.layoutMargins = UIEdgeInsets(top: 16, left: 0, bottom: 0, right: 0) @@ -297,6 +297,13 @@ extension NotificationView { } +// MARK: - AdaptiveContainerView +extension NotificationView: AdaptiveContainerView { + public func updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: Bool) { + statusView.updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: isEnabled) + } +} + extension NotificationView { public typealias AuthorMenuContext = StatusView.AuthorMenuContext diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusMetricView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusMetricView.swift index 7b356fc6..2a770f30 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusMetricView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusMetricView.swift @@ -71,8 +71,8 @@ extension StatusMetricView { addSubview(containerStackView) NSLayoutConstraint.activate([ containerStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), - containerStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), - containerStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor), + containerStackView.leadingAnchor.constraint(equalTo: leadingAnchor), + containerStackView.trailingAnchor.constraint(equalTo: trailingAnchor), bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 12), ]) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift index 2b9f936b..e77e33b2 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift @@ -24,19 +24,15 @@ public protocol StatusViewDelegate: AnyObject { 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, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView) - // func statusView(_ statusView: StatusView, spoilerBannerViewDidPressed bannerView: SpoilerBannerView) func statusView(_ statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaSensitiveButtonDidPressed button: UIButton) // a11y func statusView(_ statusView: StatusView, accessibilityActivate: Void) - -// func statusView(_ statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) -// func statusView(_ statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) } public final class StatusView: UIView { - public static let containerLayoutMargin = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) + public static let containerLayoutMargin: CGFloat = 16 let logger = Logger(subsystem: "StatusView", category: "View") @@ -61,6 +57,7 @@ public final class StatusView: UIView { }() // header + let headerAdaptiveMarginContainerView = AdaptiveMarginContainerView() public let headerContainerView = UIView() // header icon @@ -75,6 +72,7 @@ public final class StatusView: UIView { let headerInfoLabel = MetaLabel(style: .statusHeader) // author + let authorAdaptiveMarginContainerView = AdaptiveMarginContainerView() let authorContainerView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal @@ -122,6 +120,7 @@ public final class StatusView: UIView { }() // content + let contentAdaptiveMarginContainerView = AdaptiveMarginContainerView() let contentContainer = UIStackView() public let contentMetaText: MetaText = { let metaText = MetaText() @@ -160,7 +159,8 @@ public final class StatusView: UIView { public let mediaGridContainerView = MediaGridContainerView() // poll - public let pollContainerView = UIStackView() + let pollAdaptiveMarginContainerView = AdaptiveMarginContainerView() + let pollContainerView = UIStackView() public let pollTableView: UITableView = { let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) tableView.register(PollOptionTableViewCell.self, forCellReuseIdentifier: String(describing: PollOptionTableViewCell.self)) @@ -213,17 +213,13 @@ public final class StatusView: UIView { indicatorView.stopAnimating() return indicatorView }() - - // visibility - public let statusVisibilityView = StatusVisibilityView() - - // spoiler banner - // public let spoilerBannerView = SpoilerBannerView() - + // toolbar + let actionToolbarAdaptiveMarginContainerView = AdaptiveMarginContainerView() public let actionToolbarContainer = ActionToolbarContainer() // metric + let statusMetricViewAdaptiveMarginContainerView = AdaptiveMarginContainerView() public let statusMetricView = StatusMetricView() // filter hint @@ -252,13 +248,12 @@ public final class StatusView: UIView { } } - headerContainerView.isHidden = true - contentSensitiveeToggleButton.isHidden = true + setHeaderDisplay(isDisplay: false) + setContentSensitiveeToggleButtonDisplay(isDisplay: false) setSpoilerOverlayViewHidden(isHidden: true) - mediaContainerView.isHidden = true - pollContainerView.isHidden = true - statusVisibilityView.isHidden = true - filterHintLabel.isHidden = true + setMediaDisplay(isDisplay: false) + setPollDisplay(isDisplay: false) + setFilterHintLabelDisplay(isDisplay: false) } public override init(frame: CGRect) { @@ -323,12 +318,6 @@ extension StatusView { ]) pollTableView.delegate = self 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 actionToolbarContainer.delegate = self } @@ -362,11 +351,6 @@ extension StatusView { 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 { @@ -408,22 +392,23 @@ extension StatusView.Style { private func base(statusView: StatusView) { // container: V - [ header container | author container | content container | media container | pollTableView | actionToolbarContainer ] - statusView.containerStackView.layoutMargins = StatusView.containerLayoutMargin // header container: H - [ icon | label ] - statusView.headerContainerView.preservesSuperviewLayoutMargins = true - statusView.containerStackView.addArrangedSubview(statusView.headerContainerView) + statusView.headerAdaptiveMarginContainerView.contentView = statusView.headerContainerView + statusView.headerAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin + statusView.containerStackView.addArrangedSubview(statusView.headerAdaptiveMarginContainerView) + statusView.headerIconImageView.translatesAutoresizingMaskIntoConstraints = false statusView.headerInfoLabel.translatesAutoresizingMaskIntoConstraints = false statusView.headerContainerView.addSubview(statusView.headerIconImageView) statusView.headerContainerView.addSubview(statusView.headerInfoLabel) NSLayoutConstraint.activate([ - statusView.headerIconImageView.leadingAnchor.constraint(equalTo: statusView.headerContainerView.layoutMarginsGuide.leadingAnchor), + statusView.headerIconImageView.leadingAnchor.constraint(equalTo: statusView.headerContainerView.leadingAnchor), statusView.headerIconImageView.heightAnchor.constraint(equalTo: statusView.headerInfoLabel.heightAnchor, multiplier: 1.0).priority(.required - 1), statusView.headerIconImageView.widthAnchor.constraint(equalTo: statusView.headerIconImageView.heightAnchor, multiplier: 1.0).priority(.required - 1), statusView.headerInfoLabel.topAnchor.constraint(equalTo: statusView.headerContainerView.topAnchor), statusView.headerInfoLabel.leadingAnchor.constraint(equalTo: statusView.headerIconImageView.trailingAnchor, constant: 6), - statusView.headerInfoLabel.trailingAnchor.constraint(equalTo: statusView.headerContainerView.layoutMarginsGuide.trailingAnchor), + statusView.headerInfoLabel.trailingAnchor.constraint(equalTo: statusView.headerContainerView.trailingAnchor), statusView.headerInfoLabel.bottomAnchor.constraint(equalTo: statusView.headerContainerView.bottomAnchor), statusView.headerInfoLabel.centerYAnchor.constraint(equalTo: statusView.headerIconImageView.centerYAnchor), ]) @@ -434,9 +419,10 @@ extension StatusView.Style { statusView.headerIconImageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) // author container: H - [ avatarButton | author meta container | contentWarningToggleButton ] - statusView.authorContainerView.preservesSuperviewLayoutMargins = true - statusView.authorContainerView.isLayoutMarginsRelativeArrangement = true - statusView.containerStackView.addArrangedSubview(statusView.authorContainerView) + statusView.authorAdaptiveMarginContainerView.contentView = statusView.authorContainerView + statusView.authorAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin + statusView.containerStackView.addArrangedSubview(statusView.authorAdaptiveMarginContainerView) + UIContentSizeCategory.publisher .sink { category in statusView.authorContainerView.axis = category > .accessibilityLarge ? .vertical : .horizontal @@ -514,9 +500,9 @@ extension StatusView.Style { statusView.contentContainer.distribution = .fill statusView.contentContainer.alignment = .top - statusView.contentContainer.preservesSuperviewLayoutMargins = true - statusView.contentContainer.isLayoutMarginsRelativeArrangement = true - statusView.containerStackView.addArrangedSubview(statusView.contentContainer) + statusView.contentAdaptiveMarginContainerView.contentView = statusView.contentContainer + statusView.contentAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin + statusView.containerStackView.addArrangedSubview(statusView.contentAdaptiveMarginContainerView) statusView.contentContainer.setContentHuggingPriority(.required - 1, for: .vertical) statusView.contentContainer.setContentCompressionResistancePriority(.required - 1, for: .vertical) @@ -533,7 +519,12 @@ extension StatusView.Style { ]) // media container: V - [ mediaGridContainerView ] + statusView.mediaContainerView.translatesAutoresizingMaskIntoConstraints = false statusView.containerStackView.addArrangedSubview(statusView.mediaContainerView) + NSLayoutConstraint.activate([ + statusView.mediaContainerView.leadingAnchor.constraint(equalTo: statusView.containerStackView.leadingAnchor), + statusView.mediaContainerView.trailingAnchor.constraint(equalTo: statusView.containerStackView.trailingAnchor), + ]) statusView.mediaGridContainerView.translatesAutoresizingMaskIntoConstraints = false statusView.mediaContainerView.addSubview(statusView.mediaGridContainerView) @@ -545,10 +536,10 @@ extension StatusView.Style { ]) // pollContainerView: V - [ pollTableView | pollStatusStackView ] + statusView.pollAdaptiveMarginContainerView.contentView = statusView.pollContainerView + statusView.pollAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin statusView.pollContainerView.axis = .vertical - statusView.pollContainerView.preservesSuperviewLayoutMargins = true - statusView.pollContainerView.isLayoutMarginsRelativeArrangement = true - statusView.containerStackView.addArrangedSubview(statusView.pollContainerView) + statusView.containerStackView.addArrangedSubview(statusView.pollAdaptiveMarginContainerView) // pollTableView statusView.pollContainerView.addArrangedSubview(statusView.pollTableView) @@ -567,18 +558,11 @@ extension StatusView.Style { statusView.pollCountdownLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) statusView.pollVoteButton.setContentHuggingPriority(.defaultHigh + 3, for: .horizontal) - // statusVisibilityView - statusView.statusVisibilityView.preservesSuperviewLayoutMargins = true - statusView.containerStackView.addArrangedSubview(statusView.statusVisibilityView) - - // spoilerBannerView - // statusView.spoilerBannerView.preservesSuperviewLayoutMargins = true - // statusView.containerStackView.addArrangedSubview(statusView.spoilerBannerView) - // action toolbar + statusView.actionToolbarAdaptiveMarginContainerView.contentView = statusView.actionToolbarContainer + statusView.actionToolbarAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin statusView.actionToolbarContainer.configure(for: .inline) - statusView.actionToolbarContainer.preservesSuperviewLayoutMargins = true - statusView.containerStackView.addArrangedSubview(statusView.actionToolbarContainer) + statusView.containerStackView.addArrangedSubview(statusView.actionToolbarAdaptiveMarginContainerView) // filterHintLabel statusView.filterHintLabel.translatesAutoresizingMaskIntoConstraints = false @@ -591,8 +575,6 @@ extension StatusView.Style { func inline(statusView: StatusView) { base(statusView: statusView) - - statusView.statusVisibilityView.removeFromSuperview() } func plain(statusView: StatusView) { @@ -600,8 +582,10 @@ extension StatusView.Style { base(statusView: statusView) // override the base style // statusMetricView - statusView.statusMetricView.layoutMargins = StatusView.containerLayoutMargin - statusView.containerStackView.addArrangedSubview(statusView.statusMetricView) + statusView.statusMetricViewAdaptiveMarginContainerView.contentView = statusView.statusMetricView + statusView.statusMetricViewAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin + statusView.containerStackView.addArrangedSubview(statusView.statusMetricViewAdaptiveMarginContainerView) + UIContentSizeCategory.publisher .sink { category in statusView.statusMetricView.containerStackView.axis = category > .accessibilityLarge ? .vertical : .horizontal @@ -614,16 +598,14 @@ extension StatusView.Style { base(statusView: statusView) // override the base style statusView.menuButton.removeFromSuperview() - statusView.statusVisibilityView.removeFromSuperview() - statusView.actionToolbarContainer.removeFromSuperview() + statusView.actionToolbarAdaptiveMarginContainerView.removeFromSuperview() } func notification(statusView: StatusView) { base(statusView: statusView) // override the base style - statusView.headerContainerView.removeFromSuperview() - statusView.authorContainerView.removeFromSuperview() - statusView.statusVisibilityView.removeFromSuperview() + statusView.headerAdaptiveMarginContainerView.removeFromSuperview() + statusView.authorAdaptiveMarginContainerView.removeFromSuperview() } func notificationQuote(statusView: StatusView) { @@ -632,8 +614,7 @@ extension StatusView.Style { statusView.contentContainer.layoutMargins.bottom = 16 // fix contentText align to edge issue statusView.contentSensitiveeToggleButton.removeFromSuperview() statusView.menuButton.removeFromSuperview() - statusView.statusVisibilityView.removeFromSuperview() - statusView.actionToolbarContainer.removeFromSuperview() + statusView.actionToolbarAdaptiveMarginContainerView.removeFromSuperview() } func composeStatusReplica(statusView: StatusView) { @@ -641,9 +622,7 @@ extension StatusView.Style { statusView.avatarButton.isUserInteractionEnabled = false statusView.menuButton.removeFromSuperview() - statusView.statusVisibilityView.removeFromSuperview() - // statusView.spoilerBannerView.removeFromSuperview() - statusView.actionToolbarContainer.removeFromSuperview() + statusView.actionToolbarAdaptiveMarginContainerView.removeFromSuperview() } func composeStatusAuthor(statusView: StatusView) { @@ -653,24 +632,22 @@ extension StatusView.Style { statusView.menuButton.removeFromSuperview() statusView.usernameTrialingDotLabel.removeFromSuperview() statusView.dateLabel.removeFromSuperview() - statusView.contentContainer.removeFromSuperview() + statusView.contentAdaptiveMarginContainerView.removeFromSuperview() statusView.spoilerOverlayView.removeFromSuperview() statusView.mediaContainerView.removeFromSuperview() - statusView.pollContainerView.removeFromSuperview() - statusView.statusVisibilityView.removeFromSuperview() - // statusView.spoilerBannerView.removeFromSuperview() - statusView.actionToolbarContainer.removeFromSuperview() + statusView.pollAdaptiveMarginContainerView.removeFromSuperview() + statusView.actionToolbarAdaptiveMarginContainerView.removeFromSuperview() } } extension StatusView { - func setHeaderDisplay() { - headerContainerView.isHidden = false + func setHeaderDisplay(isDisplay: Bool = true) { + headerAdaptiveMarginContainerView.isHidden = !isDisplay } - func setContentSensitiveeToggleButtonDisplay() { - contentSensitiveeToggleButton.isHidden = false + func setContentSensitiveeToggleButtonDisplay(isDisplay: Bool = true) { + contentSensitiveeToggleButton.isHidden = !isDisplay } func setSpoilerOverlayViewHidden(isHidden: Bool) { @@ -678,31 +655,35 @@ extension StatusView { spoilerOverlayView.setComponentHidden(isHidden) } - func setMediaDisplay() { - mediaContainerView.isHidden = false + func setMediaDisplay(isDisplay: Bool = true) { + mediaContainerView.isHidden = !isDisplay } - func setPollDisplay() { - pollContainerView.isHidden = false + func setPollDisplay(isDisplay: Bool = true) { + pollAdaptiveMarginContainerView.isHidden = !isDisplay } - func setVisibilityDisplay() { - statusVisibilityView.isHidden = false + func setFilterHintLabelDisplay(isDisplay: Bool = true) { + filterHintLabel.isHidden = !isDisplay } - func setFilterHintLabelDisplay() { - filterHintLabel.isHidden = false - } - - // content text Width + // container width public var contentMaxLayoutWidth: CGFloat { - let inset = contentLayoutInset - return frame.width - inset.left - inset.right + return frame.width } - - public var contentLayoutInset: UIEdgeInsets { - // TODO: adaptive iPad regular horizontal size class - return .zero + +} + +// MARK: - AdaptiveContainerView +extension StatusView: AdaptiveContainerView { + public func updateContainerViewComponentsLayoutMarginsRelativeArrangementBehavior(isEnabled: Bool) { + let margin = isEnabled ? StatusView.containerLayoutMargin : .zero + headerAdaptiveMarginContainerView.margin = margin + authorAdaptiveMarginContainerView.margin = margin + contentAdaptiveMarginContainerView.margin = margin + pollAdaptiveMarginContainerView.margin = margin + actionToolbarAdaptiveMarginContainerView.margin = margin + statusMetricViewAdaptiveMarginContainerView.margin = margin } } diff --git a/MastodonSDK/Sources/MastodonUI/View/Control/ActionToolbarContainer.swift b/MastodonSDK/Sources/MastodonUI/View/Control/ActionToolbarContainer.swift index c3a7ba37..449254d2 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Control/ActionToolbarContainer.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Control/ActionToolbarContainer.swift @@ -48,10 +48,7 @@ public final class ActionToolbarContainer: UIView { extension ActionToolbarContainer { - private func _init() { - container.preservesSuperviewLayoutMargins = true - container.isLayoutMarginsRelativeArrangement = true - + private func _init() { container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) NSLayoutConstraint.activate([