diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 18a99202..18d228c2 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -412,6 +412,7 @@ DB9E0D6F25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */; }; DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */; }; DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; }; + DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA1DB7F268F84F80052DB59 /* NotificationType.swift */; }; DBA5E7A3263AD0A3004598BB /* PhotoLibraryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */; }; DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */; }; DBA5E7A9263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */; }; @@ -1031,6 +1032,7 @@ DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIInterpolatingMotionEffect.swift; sourceTree = ""; }; DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedEdgesButton.swift; sourceTree = ""; }; DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = ""; }; + DBA1DB7F268F84F80052DB59 /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = ""; }; DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryService.swift; sourceTree = ""; }; DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewModel.swift; sourceTree = ""; }; DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewController.swift; sourceTree = ""; }; @@ -1776,6 +1778,7 @@ DB6D9F4F2635761F008423CD /* SubscriptionAlerts.swift */, DBAFB7342645463500371D5F /* Emojis.swift */, DBA94439265CC0FC00C537E1 /* Fields.swift */, + DBA1DB7F268F84F80052DB59 /* NotificationType.swift */, ); path = CoreDataStack; sourceTree = ""; @@ -3181,6 +3184,7 @@ DBAC648F267DC84D007FE9FD /* TableNodeDiffableDataSource.swift in Sources */, 2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */, 0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */, + DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */, DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */, DBAC649B267DF8C8007FE9FD /* ActivityIndicatorNode.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index f1135b12..45da78cb 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 - 26 + 19 CoreDataStack.xcscheme_^#shared#^_ @@ -37,7 +37,7 @@ NotificationService.xcscheme_^#shared#^_ orderHint - 21 + 18 SuppressBuildableAutocreation diff --git a/Mastodon/Diffiable/Section/NotificationSection.swift b/Mastodon/Diffiable/Section/NotificationSection.swift index 820f9d4b..0289bc4f 100644 --- a/Mastodon/Diffiable/Section/NotificationSection.swift +++ b/Mastodon/Diffiable/Section/NotificationSection.swift @@ -20,7 +20,6 @@ enum NotificationSection: Equatable, Hashable { extension NotificationSection { static func tableViewDiffableDataSource( for tableView: UITableView, - timestampUpdatePublisher: AnyPublisher, managedObjectContext: NSManagedObjectContext, delegate: NotificationTableViewCellDelegate, dependency: NeedsDependency @@ -34,73 +33,46 @@ extension NotificationSection { guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { return UITableViewCell() } - guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else { - // filter out invalid type using predicate - assertionFailure() - return UITableViewCell() + + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationStatusTableViewCell.self), for: indexPath) as! NotificationStatusTableViewCell + cell.delegate = delegate + + // configure author + cell.avatarImageViewTask = Nuke.loadImage( + with: notification.account.avatarImageURL(), + options: ImageLoadingOptions( + placeholder: UIImage.placeholder(color: .systemFill), + transition: .fadeIn(duration: 0.2) + ), + into: cell.avatarImageView + ) + cell.avatarImageView.gesture().sink { [weak cell] _ in + cell?.delegate?.userAvatarDidPressed(notification: notification) } - - let createAt = notification.createAt - let timeText = createAt.timeAgoSinceNow - - let actionText = type.actionText - let actionImageName = type.actionImageName - let color = type.color - - if let status = notification.status { - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationStatusTableViewCell.self), for: indexPath) as! NotificationStatusTableViewCell - cell.delegate = delegate - let activeMastodonAuthenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value - let requestUserID = activeMastodonAuthenticationBox?.userID ?? "" - let frame = CGRect(x: 0, y: 0, width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right, height: tableView.readableContentGuide.layoutFrame.height) - StatusSection.configure( - cell: cell, - tableView: tableView, - dependency: dependency, - readableLayoutFrame: frame, - status: status, - requestUserID: requestUserID, - statusItemAttribute: attribute + .store(in: &cell.disposeBag) + cell.actionImageView.image = UIImage( + systemName: notification.notificationType.actionImageName, + withConfiguration: UIImage.SymbolConfiguration( + pointSize: 12, weight: .semibold ) - cell.actionImageBackground.backgroundColor = color - cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) - cell.actionLabel.text = actionText + " · " + timeText - timestampUpdatePublisher - .sink { [weak cell] _ in - guard let cell = cell else { return } - let timeText = createAt.timeAgoSinceNow - cell.actionLabel.text = actionText + " · " + timeText - } - .store(in: &cell.disposeBag) - if let url = notification.account.avatarImageURL() { - cell.avatarImageViewTask = Nuke.loadImage( - with: url, - options: ImageLoadingOptions( - placeholder: UIImage.placeholder(color: .systemFill), - transition: .fadeIn(duration: 0.2) - ), - into: cell.avatarImageView - ) - } - cell.avatarImageView.gesture().sink { [weak cell] _ in - cell?.delegate?.userAvatarDidPressed(notification: notification) + )?.withRenderingMode(.alwaysTemplate) + cell.actionImageBackground.backgroundColor = notification.notificationType.color + + // configure author name, notification description, timestamp + cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) + let createAt = notification.createAt + let actionText = notification.notificationType.actionText + cell.actionLabel.text = actionText + " · " + createAt.timeAgoSinceNow + AppContext.shared.timestampUpdatePublisher + .receive(on: DispatchQueue.main) + .sink { [weak cell] _ in + guard let cell = cell else { return } + cell.actionLabel.text = actionText + " · " + createAt.timeAgoSinceNow } .store(in: &cell.disposeBag) - if let actionImage = UIImage(systemName: actionImageName, withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .semibold))?.withRenderingMode(.alwaysTemplate) { - cell.actionImageView.image = actionImage - } - return cell - - } else { - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell - cell.delegate = delegate - timestampUpdatePublisher - .sink { [weak cell] _ in - guard let cell = cell else { return } - let timeText = createAt.timeAgoSinceNow - cell.actionLabel.text = actionText + " · " + timeText - } - .store(in: &cell.disposeBag) + + // configure follow request (if exist) + if case .followRequest = notification.notificationType { cell.acceptButton.publisher(for: .touchUpInside) .sink { [weak cell] _ in guard let cell = cell else { return } @@ -113,29 +85,46 @@ extension NotificationSection { cell.delegate?.notificationTableViewCell(cell, notification: notification, rejectButtonDidPressed: cell.rejectButton) } .store(in: &cell.disposeBag) - cell.actionImageBackground.backgroundColor = color - cell.actionLabel.text = actionText + " · " + timeText - cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict) - if let url = notification.account.avatarImageURL() { - cell.avatarImageViewTask = Nuke.loadImage( - with: url, - options: ImageLoadingOptions( - placeholder: UIImage.placeholder(color: .systemFill), - transition: .fadeIn(duration: 0.2) - ), - into: cell.avatarImageView - ) - } - cell.avatarImageView.gesture().sink { [weak cell] _ in - cell?.delegate?.userAvatarDidPressed(notification: notification) - } - .store(in: &cell.disposeBag) - if let actionImage = UIImage(systemName: actionImageName, withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .semibold))?.withRenderingMode(.alwaysTemplate) { - cell.actionImageView.image = actionImage - } - cell.buttonStackView.isHidden = (type != .followRequest) - return cell + cell.buttonStackView.isHidden = false + } else { + cell.buttonStackView.isHidden = true } + + // configure status (if exist) + if let status = notification.status { + let frame = CGRect( + x: 0, + y: 0, + width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right, + height: tableView.readableContentGuide.layoutFrame.height + ) + StatusSection.configure( + cell: cell, + tableView: tableView, + dependency: dependency, + readableLayoutFrame: frame, + status: status, + requestUserID: notification.userID, + statusItemAttribute: attribute + ) + cell.containerStackView.alignment = .top + cell.containerStackViewBottomLayoutConstraint.constant = 0 + } else { + if case .followRequest = notification.notificationType { + cell.containerStackView.alignment = .top + } else { + cell.containerStackView.alignment = .center + } + cell.statusContainerView.isHidden = true + cell.containerStackViewBottomLayoutConstraint.constant = 5 // 5pt margin when no status view + } + + // make constraint change take effect + cell.setNeedsLayout() + cell.layoutIfNeeded() + + return cell + case .bottomLoader: let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) as! TimelineBottomLoaderTableViewCell cell.startAnimating() diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index 35dd535c..ef6e1422 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -512,13 +512,8 @@ extension StatusSection { let name = author.displayName.isEmpty ? author.username : author.displayName return L10n.Common.Controls.Status.userReblogged(name) }() - MastodonStatusContent.parseResult(content: headerText, emojiDict: status.author.emojiDict) - .receive(on: DispatchQueue.main) - .sink { [weak cell] parseResult in - guard let cell = cell else { return } - cell.statusView.headerInfoLabel.configure(contentParseResult: parseResult) - } - .store(in: &cell.disposeBag) + // sync set display name to avoid layout issue + cell.statusView.headerInfoLabel.configure(content: headerText, emojiDict: status.author.emojiDict) cell.statusView.headerInfoLabel.accessibilityLabel = headerText cell.statusView.headerInfoLabel.isAccessibilityElement = true } else if status.inReplyToID != nil { diff --git a/Mastodon/Extension/CoreDataStack/NotificationType.swift b/Mastodon/Extension/CoreDataStack/NotificationType.swift new file mode 100644 index 00000000..d954563a --- /dev/null +++ b/Mastodon/Extension/CoreDataStack/NotificationType.swift @@ -0,0 +1,16 @@ +// +// NotificationType.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-7-3. +// + +import Foundation +import CoreDataStack +import MastodonSDK + +extension MastodonNotification { + var notificationType: Mastodon.Entity.Notification.NotificationType { + return Mastodon.Entity.Notification.NotificationType(rawValue: typeRaw) ?? ._other(typeRaw) + } +} diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index da2eee46..376ea4b4 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -40,10 +40,10 @@ internal enum Asset { internal static let disabled = ColorAsset(name: "Colors/Background/Poll/disabled") } internal static let alertYellow = ColorAsset(name: "Colors/Background/alert.yellow") + internal static let bar = ColorAsset(name: "Colors/Background/bar") internal static let dangerBorder = ColorAsset(name: "Colors/Background/danger.border") internal static let danger = ColorAsset(name: "Colors/Background/danger") internal static let mediaTypeIndicotor = ColorAsset(name: "Colors/Background/media.type.indicotor") - internal static let navigationBar = ColorAsset(name: "Colors/Background/navigationBar") internal static let onboardingBackground = ColorAsset(name: "Colors/Background/onboarding.background") internal static let secondaryGroupedSystemBackground = ColorAsset(name: "Colors/Background/secondary.grouped.system.background") internal static let secondarySystemBackground = ColorAsset(name: "Colors/Background/secondary.system.background") @@ -54,8 +54,10 @@ internal enum Asset { internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Colors/Background/tertiary.system.grouped.background") } internal enum Border { - internal static let notification = ColorAsset(name: "Colors/Border/notification") + internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll") + internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status") internal static let searchCard = ColorAsset(name: "Colors/Border/searchCard") + internal static let status = ColorAsset(name: "Colors/Border/status") } internal enum Button { internal static let actionToolbar = ColorAsset(name: "Colors/Button/action.toolbar") @@ -84,6 +86,9 @@ internal enum Asset { internal enum Slider { internal static let track = ColorAsset(name: "Colors/Slider/track") } + internal enum TabBar { + internal static let itemInactive = ColorAsset(name: "Colors/TabBar/item.inactive") + } internal enum TextField { internal static let background = ColorAsset(name: "Colors/TextField/background") internal static let invalid = ColorAsset(name: "Colors/TextField/invalid") diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Background/navigationBar.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Background/bar.colorset/Contents.json similarity index 83% rename from Mastodon/Resources/Assets.xcassets/Colors/Background/navigationBar.colorset/Contents.json rename to Mastodon/Resources/Assets.xcassets/Colors/Background/bar.colorset/Contents.json index 7f9578a7..c3ec51c8 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Background/navigationBar.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Background/bar.colorset/Contents.json @@ -22,10 +22,10 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.940", - "blue" : "29", - "green" : "29", - "red" : "29" + "alpha" : "1.000", + "blue" : "0.263", + "green" : "0.208", + "red" : "0.192" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Background/secondary.grouped.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Background/secondary.grouped.system.background.colorset/Contents.json index 69752587..acd80352 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Background/secondary.grouped.system.background.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Background/secondary.grouped.system.background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x43", - "green" : "0x35", - "red" : "0x31" + "blue" : "67", + "green" : "53", + "red" : "49" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.background.colorset/Contents.json index b769998d..c19f25c8 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.background.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.173", - "red" : "0.157" + "blue" : "55", + "green" : "44", + "red" : "40" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.grouped.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.grouped.background.colorset/Contents.json index 06932c34..66083b13 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.grouped.background.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Background/tertiary.system.grouped.background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.263", - "green" : "0.208", - "red" : "0.192" + "blue" : "67", + "green" : "53", + "red" : "49" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Border/compose.poll.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Border/compose.poll.colorset/Contents.json new file mode 100644 index 00000000..6e7ccba3 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/Border/compose.poll.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.431", + "green" : "0.341", + "red" : "0.310" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json similarity index 77% rename from Mastodon/Resources/Assets.xcassets/Colors/Border/notification.colorset/Contents.json rename to Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json index afc18df1..1067c15d 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Border/notification.status.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0xE8", - "green" : "0xE1", - "red" : "0xD9" + "blue" : "232", + "green" : "225", + "red" : "217" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "60", - "green" : "58", - "red" : "58" + "blue" : "110", + "green" : "87", + "red" : "79" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Border/status.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Border/status.colorset/Contents.json new file mode 100644 index 00000000..486f8649 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/Border/status.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.003", + "blue" : "213", + "green" : "213", + "red" : "213" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Button/inactive.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Button/inactive.colorset/Contents.json index 9fbab220..717d7892 100644 --- a/Mastodon/Resources/Assets.xcassets/Colors/Button/inactive.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Colors/Button/inactive.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.549", - "green" : "0.510", - "red" : "0.431" + "blue" : "140", + "green" : "130", + "red" : "110" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.392", - "green" : "0.365", - "red" : "0.310" + "blue" : "100", + "green" : "93", + "red" : "79" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json new file mode 100644 index 00000000..6e965652 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/TabBar/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json new file mode 100644 index 00000000..48fc40b0 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Colors/TabBar/item.inactive.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "140", + "green" : "130", + "red" : "110" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "200", + "green" : "174", + "red" : "155" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mastodon/Scene/MainTab/MainTabBarController.swift b/Mastodon/Scene/MainTab/MainTabBarController.swift index db409ce6..3ef16785 100644 --- a/Mastodon/Scene/MainTab/MainTabBarController.swift +++ b/Mastodon/Scene/MainTab/MainTabBarController.swift @@ -100,7 +100,7 @@ extension MainTabBarController { delegate = self - view.backgroundColor = .systemBackground + view.backgroundColor = Asset.Colors.Background.systemBackground.color let tabs = Tab.allCases let viewControllers: [UIViewController] = tabs.map { tab in @@ -113,13 +113,6 @@ extension MainTabBarController { setViewControllers(viewControllers, animated: false) selectedIndex = 0 - // TODO: custom accent color - let tabBarAppearance = UITabBarAppearance() - tabBarAppearance.configureWithDefaultBackground() - tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color - tabBar.standardAppearance = tabBarAppearance - - context.apiService.error .receive(on: DispatchQueue.main) .sink { [weak self] error in diff --git a/Mastodon/Scene/Notification/NotificationViewController.swift b/Mastodon/Scene/Notification/NotificationViewController.swift index 200ea2f2..9772f516 100644 --- a/Mastodon/Scene/Notification/NotificationViewController.swift +++ b/Mastodon/Scene/Notification/NotificationViewController.swift @@ -32,11 +32,8 @@ final class NotificationViewController: UIViewController, NeedsDependency { let tableView: UITableView = { let tableView = ControlContainableTableView() - tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - tableView.register(NotificationTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationTableViewCell.self)) tableView.register(NotificationStatusTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationStatusTableViewCell.self)) tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) - tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = UITableView.automaticDimension tableView.separatorStyle = .none tableView.tableFooterView = UIView() @@ -274,11 +271,11 @@ extension NotificationViewController: ContentOffsetAdjustableTimelineViewControl // MARK: - NotificationTableViewCellDelegate extension NotificationViewController: NotificationTableViewCellDelegate { - func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) { + func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) { viewModel.acceptFollowRequest(notification: notification) } - func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) { + func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) { viewModel.rejectFollowRequest(notification: notification) } diff --git a/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift b/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift index b8c70f3b..f0dbd250 100644 --- a/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift +++ b/Mastodon/Scene/Notification/NotificationViewModel+Diffable.swift @@ -17,14 +17,8 @@ extension NotificationViewModel { delegate: NotificationTableViewCellDelegate, dependency: NeedsDependency ) { - let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common) - .autoconnect() - .share() - .eraseToAnyPublisher() - diffableDataSource = NotificationSection.tableViewDiffableDataSource( for: tableView, - timestampUpdatePublisher: timestampUpdatePublisher, managedObjectContext: context.managedObjectContext, delegate: delegate, dependency: dependency diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift index 83fa5009..a015fb5c 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift @@ -15,12 +15,17 @@ import FLAnimatedImage import Nuke final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { + static let actionImageBorderWidth: CGFloat = 2 static let statusPadding = UIEdgeInsets(top: 50, left: 73, bottom: 24, right: 24) + var disposeBag = Set() var pollCountdownSubscription: AnyCancellable? var delegate: NotificationTableViewCellDelegate? + var containerStackViewBottomLayoutConstraint: NSLayoutConstraint! + let containerStackView = UIStackView() + var avatarImageViewTask: ImageTask? let avatarImageView: UIImageView = { let imageView = FLAnimatedImageView() @@ -51,7 +56,9 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { let view = UIView() return view }() - + + let contentStackView = UIStackView() + let actionLabel: UILabel = { let label = UILabel() label.textColor = Asset.Colors.Label.secondary.color @@ -67,18 +74,34 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell { label.lineBreakMode = .byTruncatingTail return label }() - - let statusBorder: UIView = { + + let buttonStackView = UIStackView() + + let acceptButton: UIButton = { + let button = UIButton(type: .custom) + let actionImage = UIImage(systemName: "checkmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) + button.setImage(actionImage, for: .normal) + button.tintColor = Asset.Colors.Label.secondary.color + return button + }() + + let rejectButton: UIButton = { + let button = UIButton(type: .custom) + let actionImage = UIImage(systemName: "xmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) + button.setImage(actionImage, for: .normal) + button.tintColor = Asset.Colors.Label.secondary.color + return button + }() + + let statusContainerView: UIView = { let view = UIView() - view.backgroundColor = .clear + view.layer.masksToBounds = true view.layer.cornerRadius = 6 - view.layer.borderWidth = 2 view.layer.cornerCurve = .continuous - view.layer.borderColor = Asset.Colors.Border.notification.color.cgColor - view.clipsToBounds = true + view.layer.borderWidth = 2 + view.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor return view }() - let statusView = StatusView() let separatorLine = UIView.separatorLine @@ -120,91 +143,102 @@ extension NotificationStatusTableViewCell { view.backgroundColor = Asset.Colors.Background.Cell.highlight.color return view }() - let containerStackView = UIStackView() + containerStackView.axis = .horizontal containerStackView.alignment = .top - containerStackView.spacing = 4 + containerStackView.distribution = .fill + containerStackView.spacing = 14 + 2 // 2pt for status container outline border containerStackView.layoutMargins = UIEdgeInsets(top: 14, left: 0, bottom: 12, right: 0) containerStackView.isLayoutMarginsRelativeArrangement = true containerStackView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(containerStackView) + containerStackViewBottomLayoutConstraint = contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor) NSLayoutConstraint.activate([ containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor), containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor), - contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor), + containerStackViewBottomLayoutConstraint.priority(.required - 1), ]) containerStackView.addArrangedSubview(avatarContainer) - avatarContainer.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - avatarContainer.heightAnchor.constraint(equalToConstant: 47).priority(.required - 1), - avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1) - ]) - - avatarContainer.addSubview(avatarImageView) avatarImageView.translatesAutoresizingMaskIntoConstraints = false + avatarContainer.addSubview(avatarImageView) NSLayoutConstraint.activate([ + avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), + avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor), + avatarImageView.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor), + avatarImageView.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor), avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), - avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), - avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) ]) - avatarContainer.addSubview(actionImageBackground) actionImageBackground.translatesAutoresizingMaskIntoConstraints = false + avatarContainer.addSubview(actionImageBackground) NSLayoutConstraint.activate([ - actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor), - actionImageBackground.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor) + actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1), + actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1), + actionImageBackground.centerYAnchor.constraint(equalTo: avatarImageView.bottomAnchor), + actionImageBackground.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor), ]) - avatarContainer.addSubview(actionImageView) actionImageView.translatesAutoresizingMaskIntoConstraints = false + actionImageBackground.addSubview(actionImageView) NSLayoutConstraint.activate([ actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor), - actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor) + actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor), ]) + containerStackView.addArrangedSubview(contentStackView) + contentStackView.axis = .vertical + contentStackView.spacing = 6 + + // header let actionStackView = UIStackView() + contentStackView.addArrangedSubview(actionStackView) actionStackView.axis = .horizontal actionStackView.distribution = .fill actionStackView.spacing = 4 - actionStackView.translatesAutoresizingMaskIntoConstraints = false - - nameLabel.translatesAutoresizingMaskIntoConstraints = false - actionStackView.addArrangedSubview(nameLabel) - actionLabel.translatesAutoresizingMaskIntoConstraints = false - actionStackView.addArrangedSubview(actionLabel) - nameLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical) - nameLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) - actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) - let statusStackView = UIStackView() - statusStackView.axis = .vertical - - statusStackView.distribution = .fill - statusStackView.spacing = 4 - statusStackView.translatesAutoresizingMaskIntoConstraints = false - statusView.translatesAutoresizingMaskIntoConstraints = false - statusStackView.addArrangedSubview(actionStackView) - - statusBorder.translatesAutoresizingMaskIntoConstraints = false - statusView.translatesAutoresizingMaskIntoConstraints = false - statusBorder.addSubview(statusView) - NSLayoutConstraint.activate([ - statusView.topAnchor.constraint(equalTo: statusBorder.topAnchor, constant: 12), - statusView.leadingAnchor.constraint(equalTo: statusBorder.leadingAnchor, constant: 12), - statusBorder.bottomAnchor.constraint(equalTo: statusView.bottomAnchor, constant: 12), - statusBorder.trailingAnchor.constraint(equalTo: statusView.trailingAnchor, constant: 12), - ]) - - statusView.delegate = self - - statusStackView.addArrangedSubview(statusBorder) - containerStackView.addArrangedSubview(statusStackView) - + actionStackView.addArrangedSubview(nameLabel) + actionStackView.addArrangedSubview(actionLabel) + nameLabel.setContentHuggingPriority(.required - 1, for: .horizontal) + nameLabel.setContentHuggingPriority(.required - 1, for: .vertical) + nameLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal) + nameLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical) + actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) + + // follow request + contentStackView.addArrangedSubview(buttonStackView) + buttonStackView.addArrangedSubview(acceptButton) + buttonStackView.addArrangedSubview(rejectButton) + buttonStackView.axis = .horizontal + buttonStackView.distribution = .fillEqually + + // status + contentStackView.addArrangedSubview(statusContainerView) + statusContainerView.layoutMargins = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12) + statusView.translatesAutoresizingMaskIntoConstraints = false + statusContainerView.addSubview(statusView) + NSLayoutConstraint.activate([ + statusView.topAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.topAnchor), + statusView.leadingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.leadingAnchor), + statusView.trailingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.trailingAnchor), + statusView.bottomAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.bottomAnchor), + ]) + statusContainerView.backgroundColor = UIColor(dynamicProvider: { collection in + switch collection.userInterfaceStyle { + case .dark: + return Asset.Colors.Background.tertiarySystemGroupedBackground.color + default: + return .clear + } + }) + // remove item don't display + statusView.actionToolbarContainer.removeFromStackView() + // it affect stackView's height, need remove + statusView.headerContainerView.removeFromStackView() + + // adaptive separator separatorLine.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(separatorLine) separatorLineToEdgeLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) @@ -215,21 +249,18 @@ extension NotificationStatusTableViewCell { separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)), ]) - resetSeparatorLineLayout() + + statusView.delegate = self - // remove item don't display - statusView.actionToolbarContainer.removeFromStackView() - // it affect stackView's height,need remove - statusView.avatarView.removeFromStackView() - statusView.usernameLabel.removeFromStackView() + resetSeparatorLineLayout() } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) resetSeparatorLineLayout() - statusBorder.layer.borderColor = Asset.Colors.Border.notification.color.cgColor actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor + statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor } } diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift index 30b4ce1b..8bdaaa14 100644 --- a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift +++ b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift @@ -28,238 +28,238 @@ protocol NotificationTableViewCellDelegate: AnyObject { func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView) func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) - func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) - func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) + func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) + func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) } -final class NotificationTableViewCell: UITableViewCell { - static let actionImageBorderWidth: CGFloat = 2 - - var disposeBag = Set() - - var delegate: NotificationTableViewCellDelegate? - - var avatarImageViewTask: ImageTask? - let avatarImageView: UIImageView = { - let imageView = FLAnimatedImageView() - imageView.layer.cornerRadius = 4 - imageView.layer.cornerCurve = .continuous - imageView.clipsToBounds = true - return imageView - }() - - let actionImageView: UIImageView = { - let imageView = UIImageView() - imageView.tintColor = Asset.Colors.Background.systemBackground.color - return imageView - }() - - let actionImageBackground: UIView = { - let view = UIView() - view.layer.cornerRadius = (24 + NotificationTableViewCell.actionImageBorderWidth) / 2 - view.layer.cornerCurve = .continuous - view.clipsToBounds = true - view.layer.borderWidth = NotificationTableViewCell.actionImageBorderWidth - view.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor - view.tintColor = Asset.Colors.Background.systemBackground.color - return view - }() - - let avatarContainer: UIView = { - let view = UIView() - return view - }() - - let actionLabel: UILabel = { - let label = UILabel() - label.textColor = Asset.Colors.Label.secondary.color - label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular), maximumPointSize: 20) - label.lineBreakMode = .byTruncatingTail - return label - }() - - let nameLabel: ActiveLabel = { - let label = ActiveLabel() - label.textColor = Asset.Colors.brandBlue.color - label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20) - label.lineBreakMode = .byTruncatingTail - return label - }() - - let acceptButton: UIButton = { - let button = UIButton(type: .custom) - let actionImage = UIImage(systemName: "checkmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) - button.setImage(actionImage, for: .normal) - button.tintColor = Asset.Colors.Label.secondary.color - return button - }() - - let rejectButton: UIButton = { - let button = UIButton(type: .custom) - let actionImage = UIImage(systemName: "xmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) - button.setImage(actionImage, for: .normal) - button.tintColor = Asset.Colors.Label.secondary.color - return button - }() - - let buttonStackView = UIStackView() - - let separatorLine = UIView.separatorLine - - var separatorLineToEdgeLeadingLayoutConstraint: NSLayoutConstraint! - var separatorLineToEdgeTrailingLayoutConstraint: NSLayoutConstraint! - - var separatorLineToMarginLeadingLayoutConstraint: NSLayoutConstraint! - var separatorLineToMarginTrailingLayoutConstraint: NSLayoutConstraint! - - override func prepareForReuse() { - super.prepareForReuse() - avatarImageViewTask?.cancel() - avatarImageViewTask = nil - disposeBag.removeAll() - } - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - configure() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - configure() - } -} - -extension NotificationTableViewCell { - func configure() { - backgroundColor = Asset.Colors.Background.systemBackground.color - selectedBackgroundView = { - let view = UIView() - view.backgroundColor = Asset.Colors.Background.Cell.highlight.color - return view - }() - - let containerStackView = UIStackView() - containerStackView.axis = .vertical - containerStackView.alignment = .fill - containerStackView.layoutMargins = UIEdgeInsets(top: 14, left: 0, bottom: 12, right: 0) - containerStackView.isLayoutMarginsRelativeArrangement = true - containerStackView.translatesAutoresizingMaskIntoConstraints = false - contentView.addSubview(containerStackView) - NSLayoutConstraint.activate([ - containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor), - containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor), - contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor), - ]) - - let horizontalStackView = UIStackView() - horizontalStackView.translatesAutoresizingMaskIntoConstraints = false - horizontalStackView.axis = .horizontal - horizontalStackView.spacing = 6 - - horizontalStackView.addArrangedSubview(avatarContainer) - avatarContainer.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - avatarContainer.heightAnchor.constraint(equalToConstant: 47).priority(.required - 1), - avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1) - ]) - - avatarContainer.addSubview(avatarImageView) - avatarImageView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), - avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), - avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), - avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) - ]) - - avatarContainer.addSubview(actionImageBackground) - actionImageBackground.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), - actionImageBackground.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor), - actionImageBackground.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor) - ]) - - avatarContainer.addSubview(actionImageView) - actionImageView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor), - actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor) - ]) - - nameLabel.translatesAutoresizingMaskIntoConstraints = false - horizontalStackView.addArrangedSubview(nameLabel) - actionLabel.translatesAutoresizingMaskIntoConstraints = false - horizontalStackView.addArrangedSubview(actionLabel) - nameLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal) - nameLabel.setContentHuggingPriority(.required - 1, for: .horizontal) - actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) - - containerStackView.addArrangedSubview(horizontalStackView) - - buttonStackView.translatesAutoresizingMaskIntoConstraints = false - buttonStackView.axis = .horizontal - buttonStackView.distribution = .fillEqually - acceptButton.translatesAutoresizingMaskIntoConstraints = false - rejectButton.translatesAutoresizingMaskIntoConstraints = false - buttonStackView.addArrangedSubview(acceptButton) - buttonStackView.addArrangedSubview(rejectButton) - containerStackView.addArrangedSubview(buttonStackView) - - separatorLine.translatesAutoresizingMaskIntoConstraints = false - contentView.addSubview(separatorLine) - separatorLineToEdgeLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) - separatorLineToEdgeTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) - separatorLineToMarginLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor) - separatorLineToMarginTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor) - NSLayoutConstraint.activate([ - separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)), - ]) - resetSeparatorLineLayout() - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor - resetSeparatorLineLayout() - } -} - -extension NotificationTableViewCell { - - private func resetSeparatorLineLayout() { - separatorLineToEdgeLeadingLayoutConstraint.isActive = false - separatorLineToEdgeTrailingLayoutConstraint.isActive = false - separatorLineToMarginLeadingLayoutConstraint.isActive = false - separatorLineToMarginTrailingLayoutConstraint.isActive = false - - if traitCollection.userInterfaceIdiom == .phone { - // to edge - NSLayoutConstraint.activate([ - separatorLineToEdgeLeadingLayoutConstraint, - separatorLineToEdgeTrailingLayoutConstraint, - ]) - } else { - if traitCollection.horizontalSizeClass == .compact { - // to edge - NSLayoutConstraint.activate([ - separatorLineToEdgeLeadingLayoutConstraint, - separatorLineToEdgeTrailingLayoutConstraint, - ]) - } else { - // to margin - NSLayoutConstraint.activate([ - separatorLineToMarginLeadingLayoutConstraint, - separatorLineToMarginTrailingLayoutConstraint, - ]) - } - } - } - -} +//final class NotificationTableViewCell: UITableViewCell { +// static let actionImageBorderWidth: CGFloat = 2 +// +// var disposeBag = Set() +// +// var delegate: NotificationTableViewCellDelegate? +// +// var avatarImageViewTask: ImageTask? +// let avatarImageView: UIImageView = { +// let imageView = FLAnimatedImageView() +// imageView.layer.cornerRadius = 4 +// imageView.layer.cornerCurve = .continuous +// imageView.clipsToBounds = true +// return imageView +// }() +// +// let actionImageView: UIImageView = { +// let imageView = UIImageView() +// imageView.tintColor = Asset.Colors.Background.systemBackground.color +// return imageView +// }() +// +// let actionImageBackground: UIView = { +// let view = UIView() +// view.layer.cornerRadius = (24 + NotificationTableViewCell.actionImageBorderWidth) / 2 +// view.layer.cornerCurve = .continuous +// view.clipsToBounds = true +// view.layer.borderWidth = NotificationTableViewCell.actionImageBorderWidth +// view.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor +// view.tintColor = Asset.Colors.Background.systemBackground.color +// return view +// }() +// +// let avatarContainer: UIView = { +// let view = UIView() +// return view +// }() +// +// let actionLabel: UILabel = { +// let label = UILabel() +// label.textColor = Asset.Colors.Label.secondary.color +// label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular), maximumPointSize: 20) +// label.lineBreakMode = .byTruncatingTail +// return label +// }() +// +// let nameLabel: ActiveLabel = { +// let label = ActiveLabel() +// label.textColor = Asset.Colors.brandBlue.color +// label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20) +// label.lineBreakMode = .byTruncatingTail +// return label +// }() +// +// let acceptButton: UIButton = { +// let button = UIButton(type: .custom) +// let actionImage = UIImage(systemName: "checkmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) +// button.setImage(actionImage, for: .normal) +// button.tintColor = Asset.Colors.Label.secondary.color +// return button +// }() +// +// let rejectButton: UIButton = { +// let button = UIButton(type: .custom) +// let actionImage = UIImage(systemName: "xmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate) +// button.setImage(actionImage, for: .normal) +// button.tintColor = Asset.Colors.Label.secondary.color +// return button +// }() +// +// let buttonStackView = UIStackView() +// +// let separatorLine = UIView.separatorLine +// +// var separatorLineToEdgeLeadingLayoutConstraint: NSLayoutConstraint! +// var separatorLineToEdgeTrailingLayoutConstraint: NSLayoutConstraint! +// +// var separatorLineToMarginLeadingLayoutConstraint: NSLayoutConstraint! +// var separatorLineToMarginTrailingLayoutConstraint: NSLayoutConstraint! +// +// override func prepareForReuse() { +// super.prepareForReuse() +// avatarImageViewTask?.cancel() +// avatarImageViewTask = nil +// disposeBag.removeAll() +// } +// +// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { +// super.init(style: style, reuseIdentifier: reuseIdentifier) +// configure() +// } +// +// required init?(coder: NSCoder) { +// super.init(coder: coder) +// configure() +// } +//} +// +//extension NotificationTableViewCell { +// func configure() { +// backgroundColor = Asset.Colors.Background.systemBackground.color +// selectedBackgroundView = { +// let view = UIView() +// view.backgroundColor = Asset.Colors.Background.Cell.highlight.color +// return view +// }() +// +// let containerStackView = UIStackView() +// containerStackView.axis = .vertical +// containerStackView.alignment = .fill +// containerStackView.layoutMargins = UIEdgeInsets(top: 14, left: 0, bottom: 12, right: 0) +// containerStackView.isLayoutMarginsRelativeArrangement = true +// containerStackView.translatesAutoresizingMaskIntoConstraints = false +// contentView.addSubview(containerStackView) +// NSLayoutConstraint.activate([ +// containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor), +// containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), +// contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor), +// contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor), +// ]) +// +// let horizontalStackView = UIStackView() +// horizontalStackView.translatesAutoresizingMaskIntoConstraints = false +// horizontalStackView.axis = .horizontal +// horizontalStackView.spacing = 6 +// +// horizontalStackView.addArrangedSubview(avatarContainer) +// avatarContainer.translatesAutoresizingMaskIntoConstraints = false +// NSLayoutConstraint.activate([ +// avatarContainer.heightAnchor.constraint(equalToConstant: 47).priority(.required - 1), +// avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1) +// ]) +// +// avatarContainer.addSubview(avatarImageView) +// avatarImageView.translatesAutoresizingMaskIntoConstraints = false +// NSLayoutConstraint.activate([ +// avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1), +// avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1), +// avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor), +// avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor) +// ]) +// +// avatarContainer.addSubview(actionImageBackground) +// actionImageBackground.translatesAutoresizingMaskIntoConstraints = false +// NSLayoutConstraint.activate([ +// actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), +// actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1), +// actionImageBackground.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor), +// actionImageBackground.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor) +// ]) +// +// avatarContainer.addSubview(actionImageView) +// actionImageView.translatesAutoresizingMaskIntoConstraints = false +// NSLayoutConstraint.activate([ +// actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor), +// actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor) +// ]) +// +// nameLabel.translatesAutoresizingMaskIntoConstraints = false +// horizontalStackView.addArrangedSubview(nameLabel) +// actionLabel.translatesAutoresizingMaskIntoConstraints = false +// horizontalStackView.addArrangedSubview(actionLabel) +// nameLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal) +// nameLabel.setContentHuggingPriority(.required - 1, for: .horizontal) +// actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) +// +// containerStackView.addArrangedSubview(horizontalStackView) +// +// buttonStackView.translatesAutoresizingMaskIntoConstraints = false +// buttonStackView.axis = .horizontal +// buttonStackView.distribution = .fillEqually +// acceptButton.translatesAutoresizingMaskIntoConstraints = false +// rejectButton.translatesAutoresizingMaskIntoConstraints = false +// buttonStackView.addArrangedSubview(acceptButton) +// buttonStackView.addArrangedSubview(rejectButton) +// containerStackView.addArrangedSubview(buttonStackView) +// +// separatorLine.translatesAutoresizingMaskIntoConstraints = false +// contentView.addSubview(separatorLine) +// separatorLineToEdgeLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) +// separatorLineToEdgeTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) +// separatorLineToMarginLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor) +// separatorLineToMarginTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor) +// NSLayoutConstraint.activate([ +// separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), +// separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)), +// ]) +// resetSeparatorLineLayout() +// } +// +// override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { +// super.traitCollectionDidChange(previousTraitCollection) +// +// actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor +// resetSeparatorLineLayout() +// } +//} +// +//extension NotificationTableViewCell { +// +// private func resetSeparatorLineLayout() { +// separatorLineToEdgeLeadingLayoutConstraint.isActive = false +// separatorLineToEdgeTrailingLayoutConstraint.isActive = false +// separatorLineToMarginLeadingLayoutConstraint.isActive = false +// separatorLineToMarginTrailingLayoutConstraint.isActive = false +// +// if traitCollection.userInterfaceIdiom == .phone { +// // to edge +// NSLayoutConstraint.activate([ +// separatorLineToEdgeLeadingLayoutConstraint, +// separatorLineToEdgeTrailingLayoutConstraint, +// ]) +// } else { +// if traitCollection.horizontalSizeClass == .compact { +// // to edge +// NSLayoutConstraint.activate([ +// separatorLineToEdgeLeadingLayoutConstraint, +// separatorLineToEdgeTrailingLayoutConstraint, +// ]) +// } else { +// // to margin +// NSLayoutConstraint.activate([ +// separatorLineToMarginLeadingLayoutConstraint, +// separatorLineToMarginTrailingLayoutConstraint, +// ]) +// } +// } +// } +// +//} diff --git a/Mastodon/Scene/Search/SearchViewController.swift b/Mastodon/Scene/Search/SearchViewController.swift index 51b88e12..bc04c0b6 100644 --- a/Mastodon/Scene/Search/SearchViewController.swift +++ b/Mastodon/Scene/Search/SearchViewController.swift @@ -39,7 +39,7 @@ final class SearchViewController: UIViewController, NeedsDependency { let statusBar: UIView = { let view = UIView() - view.backgroundColor = Asset.Colors.Background.navigationBar.color + view.backgroundColor = Asset.Colors.Background.bar.color return view }() @@ -52,7 +52,7 @@ final class SearchViewController: UIViewController, NeedsDependency { // searchBar.setImage(micImage, for: .bookmark, state: .normal) // searchBar.showsBookmarkButton = true searchBar.scopeButtonTitles = [L10n.Scene.Search.Searching.Segment.all, L10n.Scene.Search.Searching.Segment.people, L10n.Scene.Search.Searching.Segment.hashtags] - searchBar.barTintColor = Asset.Colors.Background.navigationBar.color + searchBar.barTintColor = Asset.Colors.Background.bar.color return searchBar }() diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift index e7be49b3..c494ca02 100644 --- a/Mastodon/Scene/Settings/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/SettingsViewController.swift @@ -95,6 +95,7 @@ class SettingsViewController: UIViewController, NeedsDependency { tableView.delegate = self tableView.rowHeight = UITableView.automaticDimension tableView.backgroundColor = .clear + tableView.separatorColor = Asset.Colors.Background.Cell.separator.color tableView.register(SettingsAppearanceTableViewCell.self, forCellReuseIdentifier: String(describing: SettingsAppearanceTableViewCell.self)) tableView.register(SettingsToggleTableViewCell.self, forCellReuseIdentifier: String(describing: SettingsToggleTableViewCell.self)) diff --git a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift index 6a3e5d4b..2d398536 100644 --- a/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift +++ b/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift @@ -15,7 +15,7 @@ protocol PlayerContainerViewDelegate: AnyObject { } final class PlayerContainerView: UIView { - static let cornerRadius: CGFloat = 8 + static let cornerRadius: CGFloat = ContentWarningOverlayView.cornerRadius private let container = UIView() private let touchBlockingView = TouchBlockingView() diff --git a/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift index 0a822246..0b374174 100644 --- a/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift +++ b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift @@ -77,8 +77,7 @@ extension ContentWarningOverlayView { private func _init() { backgroundColor = .clear isUserInteractionEnabled = true - layer.masksToBounds = false - + // visual effect style // add blur visual effect view in the setup method blurVisualEffectView.layer.masksToBounds = true @@ -115,10 +114,10 @@ extension ContentWarningOverlayView { contentOverlayView.translatesAutoresizingMaskIntoConstraints = false addSubview(contentOverlayView) NSLayoutConstraint.activate([ - topAnchor.constraint(equalTo: contentOverlayView.topAnchor, constant: 2), - leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor, constant: 2), - contentOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 2), - contentOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 2), + topAnchor.constraint(equalTo: contentOverlayView.topAnchor), + leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor), + contentOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor), + contentOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) let blurContentWarningLabelContainer = UIStackView() diff --git a/Mastodon/Scene/Share/View/Content/StatusView.swift b/Mastodon/Scene/Share/View/Content/StatusView.swift index 7ed828fa..64697b59 100644 --- a/Mastodon/Scene/Share/View/Content/StatusView.swift +++ b/Mastodon/Scene/Share/View/Content/StatusView.swift @@ -97,8 +97,8 @@ final class StatusView: UIView { }() let avatarImageView: UIImageView = { let imageView = FLAnimatedImageView() - imageView.layer.shouldRasterize = true - imageView.layer.rasterizationScale = UIScreen.main.scale +// imageView.layer.shouldRasterize = true +// imageView.layer.rasterizationScale = UIScreen.main.scale return imageView }() let avatarStackedContainerButton: AvatarStackContainerButton = AvatarStackContainerButton() @@ -343,6 +343,7 @@ extension StatusView { let titleContainerStackView = UIStackView() authorMetaContainerStackView.addArrangedSubview(titleContainerStackView) titleContainerStackView.axis = .horizontal + titleContainerStackView.alignment = .center titleContainerStackView.spacing = 4 nameLabel.translatesAutoresizingMaskIntoConstraints = false titleContainerStackView.addArrangedSubview(nameLabel) @@ -379,7 +380,7 @@ extension StatusView { authorContainerStackView.topAnchor.constraint(equalTo: authorContainerView.topAnchor), authorContainerStackView.leadingAnchor.constraint(equalTo: authorContainerView.leadingAnchor), authorContainerStackView.trailingAnchor.constraint(equalTo: authorContainerView.trailingAnchor), - authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.defaultHigh), + authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.required - 1), ]) containerStackView.addArrangedSubview(authorContainerView) diff --git a/Mastodon/Scene/Share/View/Control/AvatarStackContainerButton.swift b/Mastodon/Scene/Share/View/Control/AvatarStackContainerButton.swift index d16a952f..83b66454 100644 --- a/Mastodon/Scene/Share/View/Control/AvatarStackContainerButton.swift +++ b/Mastodon/Scene/Share/View/Control/AvatarStackContainerButton.swift @@ -46,11 +46,12 @@ final class AvatarStackContainerButton: UIControl { extension AvatarStackContainerButton { private func _init() { - topLeadingAvatarStackedImageView.layer.shouldRasterize = true - topLeadingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale - - bottomTrailingAvatarStackedImageView.layer.shouldRasterize = true - bottomTrailingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale + // GIF get worse when enable rasterize +// topLeadingAvatarStackedImageView.layer.shouldRasterize = true +// topLeadingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale +// +// bottomTrailingAvatarStackedImageView.layer.shouldRasterize = true +// bottomTrailingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale topLeadingAvatarStackedImageView.translatesAutoresizingMaskIntoConstraints = false addSubview(topLeadingAvatarStackedImageView) diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 697e9065..f6f6341a 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -33,6 +33,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // set tint color window.tintColor = Asset.Colors.brandBlue.color + + // set navigation bar appearance + let appearance = UINavigationBarAppearance() + appearance.configureWithDefaultBackground() + appearance.backgroundColor = Asset.Colors.Background.bar.color + UINavigationBar.appearance().standardAppearance = appearance + UINavigationBar.appearance().compactAppearance = appearance + UINavigationBar.appearance().scrollEdgeAppearance = appearance + + // set tab bar appearance + let tabBarAppearance = UITabBarAppearance() + tabBarAppearance.configureWithDefaultBackground() + + let tabBarItemAppearance = UITabBarItemAppearance() + tabBarItemAppearance.selected.iconColor = Asset.Colors.brandBlue.color + tabBarItemAppearance.focused.iconColor = Asset.Colors.TabBar.itemInactive.color + tabBarItemAppearance.normal.iconColor = Asset.Colors.TabBar.itemInactive.color + tabBarItemAppearance.disabled.iconColor = Asset.Colors.TabBar.itemInactive.color + tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance + tabBarAppearance.inlineLayoutAppearance = tabBarItemAppearance + tabBarAppearance.compactInlineLayoutAppearance = tabBarItemAppearance + + tabBarAppearance.backgroundColor = Asset.Colors.Background.bar.color + tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color + UITabBar.appearance().standardAppearance = tabBarAppearance let appContext = AppContext.shared let sceneCoordinator = SceneCoordinator(scene: scene, sceneDelegate: self, appContext: appContext)