feat: update compose scene UI appearance

This commit is contained in:
CMK 2021-04-14 15:24:54 +08:00
parent 8df3b7d464
commit 0eff43e1d1
55 changed files with 257 additions and 63 deletions

View File

@ -240,6 +240,7 @@
}, },
"content_input_placeholder": "Type or paste what's on your mind", "content_input_placeholder": "Type or paste what's on your mind",
"compose_action": "Publish", "compose_action": "Publish",
"replying_to_user": "replying to %s",
"attachment": { "attachment": {
"photo": "photo", "photo": "photo",
"video": "video", "video": "video",
@ -254,7 +255,8 @@
"six_hours": "6 Hours", "six_hours": "6 Hours",
"one_day": "1 Day", "one_day": "1 Day",
"three_days": "3 Days", "three_days": "3 Days",
"seven_days": "7 Days" "seven_days": "7 Days",
"option_number": "Option %ld"
}, },
"content_warning": { "content_warning": {
"placeholder": "Write an accurate warning here..." "placeholder": "Write an accurate warning here..."
@ -336,4 +338,4 @@
} }
} }
} }
} }

View File

@ -7,7 +7,7 @@
<key>CoreDataStack.xcscheme_^#shared#^_</key> <key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>10</integer> <integer>20</integer>
</dict> </dict>
<key>Mastodon - RTL.xcscheme_^#shared#^_</key> <key>Mastodon - RTL.xcscheme_^#shared#^_</key>
<dict> <dict>

View File

@ -50,8 +50,15 @@ extension ComposeStatusSection {
weak composeStatusPollExpiresOptionCollectionViewCellDelegate weak composeStatusPollExpiresOptionCollectionViewCellDelegate
] collectionView, indexPath, item -> UICollectionViewCell? in ] collectionView, indexPath, item -> UICollectionViewCell? in
switch item { switch item {
case .replyTo(let repliedToStatusObjectID): case .replyTo(let replyToStatusObjectID):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeRepliedToStatusContentCollectionViewCell.self), for: indexPath) as! ComposeRepliedToStatusContentCollectionViewCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeRepliedToStatusContentCollectionViewCell.self), for: indexPath) as! ComposeRepliedToStatusContentCollectionViewCell
managedObjectContext.perform {
guard let replyTo = managedObjectContext.object(with: replyToStatusObjectID) as? Status else {
return
}
let status = replyTo.reblog ?? replyTo
cell.statusView.configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: status.author.avatarImageURL()))
}
return cell return cell
case .input(let replyToStatusObjectID, let attribute): case .input(let replyToStatusObjectID, let attribute):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self), for: indexPath) as! ComposeStatusContentCollectionViewCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self), for: indexPath) as! ComposeStatusContentCollectionViewCell
@ -63,9 +70,10 @@ extension ComposeStatusSection {
return return
} }
cell.statusView.headerContainerStackView.isHidden = false cell.statusView.headerContainerStackView.isHidden = false
cell.statusView.headerInfoLabel.text = "[TODO] \(replyTo.author.displayName)" cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.replyIconImage)
cell.statusView.headerInfoLabel.text = L10n.Scene.Compose.replyingToUser(replyTo.author.displayNameWithFallback)
} }
ComposeStatusSection.configure(cell: cell, attribute: attribute) ComposeStatusSection.configureStatusContent(cell: cell, attribute: attribute)
cell.textEditorView.textAttributesDelegate = textEditorViewTextAttributesDelegate cell.textEditorView.textAttributesDelegate = textEditorViewTextAttributesDelegate
cell.composeContent cell.composeContent
.removeDuplicates() .removeDuplicates()
@ -196,7 +204,7 @@ extension ComposeStatusSection {
extension ComposeStatusSection { extension ComposeStatusSection {
static func configure( static func configureStatusContent(
cell: ComposeStatusContentCollectionViewCell, cell: ComposeStatusContentCollectionViewCell,
attribute: ComposeStatusItem.ComposeStatusAttribute attribute: ComposeStatusItem.ComposeStatusAttribute
) { ) {

View File

@ -393,7 +393,7 @@ extension StatusSection {
) { ) {
if status.reblog != nil { if status.reblog != nil {
cell.statusView.headerContainerStackView.isHidden = false cell.statusView.headerContainerStackView.isHidden = false
cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.boostIconImage) cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.reblogIconImage)
cell.statusView.headerInfoLabel.text = { cell.statusView.headerInfoLabel.text = {
let author = status.author let author = status.author
let name = author.displayName.isEmpty ? author.username : author.displayName let name = author.displayName.isEmpty ? author.username : author.displayName

View File

@ -91,26 +91,32 @@ internal enum Asset {
internal enum Connectivity { internal enum Connectivity {
internal static let photoFillSplit = ImageAsset(name: "Connectivity/photo.fill.split") internal static let photoFillSplit = ImageAsset(name: "Connectivity/photo.fill.split")
} }
internal enum Profile { internal enum Scene {
internal enum Banner { internal enum Compose {
internal static let bioEditBackgroundGray = ColorAsset(name: "Profile/Banner/bio.edit.background.gray") internal static let background = ColorAsset(name: "Scene/Compose/background")
internal static let nameEditBackgroundGray = ColorAsset(name: "Profile/Banner/name.edit.background.gray") internal static let toolbarBackground = ColorAsset(name: "Scene/Compose/toolbar.background")
internal static let usernameGray = ColorAsset(name: "Profile/Banner/username.gray")
} }
} internal enum Profile {
internal enum Welcome { internal enum Banner {
internal enum Illustration { internal static let bioEditBackgroundGray = ColorAsset(name: "Scene/Profile/Banner/bio.edit.background.gray")
internal static let backgroundCyan = ColorAsset(name: "Welcome/illustration/background.cyan") internal static let nameEditBackgroundGray = ColorAsset(name: "Scene/Profile/Banner/name.edit.background.gray")
internal static let cloudBase = ImageAsset(name: "Welcome/illustration/cloud.base") internal static let usernameGray = ColorAsset(name: "Scene/Profile/Banner/username.gray")
internal static let elephantOnAirplaneWithContrail = ImageAsset(name: "Welcome/illustration/elephant.on.airplane.with.contrail") }
internal static let elephantThreeOnGrass = ImageAsset(name: "Welcome/illustration/elephant.three.on.grass") }
internal static let elephantThreeOnGrassWithTreeThree = ImageAsset(name: "Welcome/illustration/elephant.three.on.grass.with.tree.three") internal enum Welcome {
internal static let elephantThreeOnGrassWithTreeTwo = ImageAsset(name: "Welcome/illustration/elephant.three.on.grass.with.tree.two") internal enum Illustration {
internal static let backgroundCyan = ColorAsset(name: "Scene/Welcome/illustration/background.cyan")
internal static let cloudBase = ImageAsset(name: "Scene/Welcome/illustration/cloud.base")
internal static let elephantOnAirplaneWithContrail = ImageAsset(name: "Scene/Welcome/illustration/elephant.on.airplane.with.contrail")
internal static let elephantThreeOnGrass = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass")
internal static let elephantThreeOnGrassWithTreeThree = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass.with.tree.three")
internal static let elephantThreeOnGrassWithTreeTwo = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass.with.tree.two")
}
internal static let mastodonLogoBlack = ImageAsset(name: "Scene/Welcome/mastodon.logo.black")
internal static let mastodonLogoBlackLarge = ImageAsset(name: "Scene/Welcome/mastodon.logo.black.large")
internal static let mastodonLogo = ImageAsset(name: "Scene/Welcome/mastodon.logo")
internal static let mastodonLogoLarge = ImageAsset(name: "Scene/Welcome/mastodon.logo.large")
} }
internal static let mastodonLogoBlack = ImageAsset(name: "Welcome/mastodon.logo.black")
internal static let mastodonLogoBlackLarge = ImageAsset(name: "Welcome/mastodon.logo.black.large")
internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo")
internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large")
} }
} }
// swiftlint:enable identifier_name line_length nesting type_body_length type_name // swiftlint:enable identifier_name line_length nesting type_body_length type_name

View File

@ -224,6 +224,10 @@ internal enum L10n {
internal static let composeAction = L10n.tr("Localizable", "Scene.Compose.ComposeAction") internal static let composeAction = L10n.tr("Localizable", "Scene.Compose.ComposeAction")
/// Type or paste what's on your mind /// Type or paste what's on your mind
internal static let contentInputPlaceholder = L10n.tr("Localizable", "Scene.Compose.ContentInputPlaceholder") internal static let contentInputPlaceholder = L10n.tr("Localizable", "Scene.Compose.ContentInputPlaceholder")
/// replying to %@
internal static func replyingToUser(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Compose.ReplyingToUser", String(describing: p1))
}
internal enum Attachment { internal enum Attachment {
/// This %@ is broken and can't be\nuploaded to Mastodon. /// This %@ is broken and can't be\nuploaded to Mastodon.
internal static func attachmentBroken(_ p1: Any) -> String { internal static func attachmentBroken(_ p1: Any) -> String {
@ -259,6 +263,10 @@ internal enum L10n {
internal static let oneDay = L10n.tr("Localizable", "Scene.Compose.Poll.OneDay") internal static let oneDay = L10n.tr("Localizable", "Scene.Compose.Poll.OneDay")
/// 1 Hour /// 1 Hour
internal static let oneHour = L10n.tr("Localizable", "Scene.Compose.Poll.OneHour") internal static let oneHour = L10n.tr("Localizable", "Scene.Compose.Poll.OneHour")
/// Option %ld
internal static func optionNumber(_ p1: Int) -> String {
return L10n.tr("Localizable", "Scene.Compose.Poll.OptionNumber", p1)
}
/// 7 Days /// 7 Days
internal static let sevenDays = L10n.tr("Localizable", "Scene.Compose.Poll.SevenDays") internal static let sevenDays = L10n.tr("Localizable", "Scene.Compose.Poll.SevenDays")
/// 6 Hours /// 6 Hours

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.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" : "0x1E",
"green" : "0x1C",
"red" : "0x1C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "222",
"green" : "216",
"red" : "214"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "43",
"green" : "43",
"red" : "43"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -86,10 +86,12 @@ uploaded to Mastodon.";
"Scene.Compose.Poll.DurationTime" = "Duration: %@"; "Scene.Compose.Poll.DurationTime" = "Duration: %@";
"Scene.Compose.Poll.OneDay" = "1 Day"; "Scene.Compose.Poll.OneDay" = "1 Day";
"Scene.Compose.Poll.OneHour" = "1 Hour"; "Scene.Compose.Poll.OneHour" = "1 Hour";
"Scene.Compose.Poll.OptionNumber" = "Option %ld";
"Scene.Compose.Poll.SevenDays" = "7 Days"; "Scene.Compose.Poll.SevenDays" = "7 Days";
"Scene.Compose.Poll.SixHours" = "6 Hours"; "Scene.Compose.Poll.SixHours" = "6 Hours";
"Scene.Compose.Poll.ThirtyMinutes" = "30 minutes"; "Scene.Compose.Poll.ThirtyMinutes" = "30 minutes";
"Scene.Compose.Poll.ThreeDays" = "3 Days"; "Scene.Compose.Poll.ThreeDays" = "3 Days";
"Scene.Compose.ReplyingToUser" = "replying to %@";
"Scene.Compose.Title.NewPost" = "New Post"; "Scene.Compose.Title.NewPost" = "New Post";
"Scene.Compose.Title.NewReply" = "New Reply"; "Scene.Compose.Title.NewReply" = "New Reply";
"Scene.Compose.Visibility.Direct" = "Only people I mention"; "Scene.Compose.Visibility.Direct" = "Only people I mention";

View File

@ -6,9 +6,22 @@
// //
import UIKit import UIKit
import Combine
final class ComposeRepliedToStatusContentCollectionViewCell: UICollectionViewCell { final class ComposeRepliedToStatusContentCollectionViewCell: UICollectionViewCell {
var disposeBag = Set<AnyCancellable>()
let statusView = StatusView()
override func prepareForReuse() {
super.prepareForReuse()
statusView.isStatusTextSensitive = false
statusView.cleanUpContentWarning()
disposeBag.removeAll()
}
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
_init() _init()
@ -24,7 +37,19 @@ final class ComposeRepliedToStatusContentCollectionViewCell: UICollectionViewCel
extension ComposeRepliedToStatusContentCollectionViewCell { extension ComposeRepliedToStatusContentCollectionViewCell {
private func _init() { private func _init() {
backgroundColor = .clear
statusView.contentWarningBlurContentImageView.backgroundColor = Asset.Scene.Compose.background.color
statusView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(statusView)
NSLayoutConstraint.activate([
statusView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
statusView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: statusView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: statusView.bottomAnchor),
])
statusView.actionToolbarContainer.isHidden = true
} }
} }

View File

@ -31,7 +31,7 @@ final class ComposeViewController: UIViewController, NeedsDependency {
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.normal.color.withAlphaComponent(0.5)), for: .highlighted) button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.normal.color.withAlphaComponent(0.5)), for: .highlighted)
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled) button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled)
button.setTitleColor(.white, for: .normal) button.setTitleColor(.white, for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 3, left: 16, bottom: 3, right: 16) button.contentEdgeInsets = UIEdgeInsets(top: 5.5, left: 16, bottom: 5.5, right: 16) // set 28pt height
button.adjustsImageWhenHighlighted = false button.adjustsImageWhenHighlighted = false
return button return button
}() }()
@ -66,18 +66,18 @@ final class ComposeViewController: UIViewController, NeedsDependency {
return view return view
}() }()
let composeToolbarView: ComposeToolbarView = { let composeToolbarView = ComposeToolbarView()
let composeToolbarView = ComposeToolbarView()
let text = UITextView()
let inputView = UIInputView(frame: .init(x: 0, y: 0, width: 40, height: 40), inputViewStyle: .keyboard)
text.inputAccessoryView = inputView
composeToolbarView.backgroundColor = inputView.backgroundColor
return composeToolbarView
}()
var composeToolbarViewBottomLayoutConstraint: NSLayoutConstraint! var composeToolbarViewBottomLayoutConstraint: NSLayoutConstraint!
let composeToolbarBackgroundView: UIView = { let composeToolbarBackgroundView: UIView = {
let backgroundView = UIView() let backgroundView = UIView()
backgroundView.backgroundColor = .secondarySystemBackground // set keyboard background to make the keyboard blurred color fixed
backgroundView.backgroundColor = UIColor(dynamicProvider: { traitCollection -> UIColor in
// avoid elevated color
switch traitCollection.userInterfaceStyle {
case .light: return .white
default: return .black
}
})
return backgroundView return backgroundView
}() }()
@ -135,7 +135,7 @@ extension ComposeViewController {
self.title = title self.title = title
} }
.store(in: &disposeBag) .store(in: &disposeBag)
view.backgroundColor = Asset.Colors.Background.systemBackground.color view.backgroundColor = Asset.Scene.Compose.background.color
navigationItem.leftBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:))) navigationItem.leftBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
navigationItem.rightBarButtonItem = publishBarButtonItem navigationItem.rightBarButtonItem = publishBarButtonItem
publishButton.addTarget(self, action: #selector(ComposeViewController.publishBarButtonItemPressed(_:)), for: .touchUpInside) publishButton.addTarget(self, action: #selector(ComposeViewController.publishBarButtonItemPressed(_:)), for: .touchUpInside)
@ -266,13 +266,17 @@ extension ComposeViewController {
.store(in: &disposeBag) .store(in: &disposeBag)
// bind visibility toolbar UI // bind visibility toolbar UI
viewModel.selectedStatusVisibility Publishers.CombineLatest(
.receive(on: DispatchQueue.main) viewModel.selectedStatusVisibility,
.sink { [weak self] type in viewModel.traitCollectionDidChangePublisher
guard let self = self else { return } )
self.composeToolbarView.visibilityButton.setImage(type.image, for: .normal) .receive(on: DispatchQueue.main)
} .sink { [weak self] type, _ in
.store(in: &disposeBag) guard let self = self else { return }
let image = type.image(interfaceStyle: self.traitCollection.userInterfaceStyle)
self.composeToolbarView.visibilityButton.setImage(image, for: .normal)
}
.store(in: &disposeBag)
viewModel.characterCount viewModel.characterCount
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
@ -336,6 +340,12 @@ extension ComposeViewController {
} }
} }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
viewModel.traitCollectionDidChangePublisher.send()
}
} }
extension ComposeViewController { extension ComposeViewController {

View File

@ -29,6 +29,7 @@ final class ComposeViewModel {
let selectedStatusVisibility = CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>(.public) let selectedStatusVisibility = CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>(.public)
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never> let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never> let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
let traitCollectionDidChangePublisher = PassthroughSubject<Void, Never>()
// output // output
var diffableDataSource: UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>! var diffableDataSource: UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>!

View File

@ -80,8 +80,12 @@ final class ComposeToolbarView: UIView {
} }
extension ComposeToolbarView { extension ComposeToolbarView {
private func _init() { private func _init() {
backgroundColor = .secondarySystemBackground // magic keyboard color (iOS 14):
// light with white background: RGB 214 216 222
// dark with black background: RGB 43 43 43
backgroundColor = Asset.Scene.Compose.toolbarBackground.color
let stackView = UIStackView() let stackView = UIStackView()
stackView.axis = .horizontal stackView.axis = .horizontal
@ -125,9 +129,18 @@ extension ComposeToolbarView {
pollButton.addTarget(self, action: #selector(ComposeToolbarView.pollButtonDidPressed(_:)), for: .touchUpInside) pollButton.addTarget(self, action: #selector(ComposeToolbarView.pollButtonDidPressed(_:)), for: .touchUpInside)
emojiButton.addTarget(self, action: #selector(ComposeToolbarView.emojiButtonDidPressed(_:)), for: .touchUpInside) emojiButton.addTarget(self, action: #selector(ComposeToolbarView.emojiButtonDidPressed(_:)), for: .touchUpInside)
contentWarningButton.addTarget(self, action: #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:)), for: .touchUpInside) contentWarningButton.addTarget(self, action: #selector(ComposeToolbarView.contentWarningButtonDidPressed(_:)), for: .touchUpInside)
visibilityButton.menu = createVisibilityContextMenu() visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
visibilityButton.showsMenuAsPrimaryAction = true visibilityButton.showsMenuAsPrimaryAction = true
updateToolbarButtonUserInterfaceStyle()
} }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateToolbarButtonUserInterfaceStyle()
}
} }
extension ComposeToolbarView { extension ComposeToolbarView {
@ -152,9 +165,15 @@ extension ComposeToolbarView {
} }
} }
var image: UIImage { func image(interfaceStyle: UIUserInterfaceStyle) -> UIImage {
switch self { switch self {
case .public: return UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))! case .public:
switch interfaceStyle {
case .light:
return UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
default:
return UIImage(systemName: "person.3.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
}
case .unlisted: return UIImage(systemName: "eye.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))! case .unlisted: return UIImage(systemName: "eye.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))!
case .private: return UIImage(systemName: "person.crop.circle.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))! case .private: return UIImage(systemName: "person.crop.circle.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))!
case .direct: return UIImage(systemName: "at", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))! case .direct: return UIImage(systemName: "at", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium))!
@ -182,6 +201,25 @@ extension ComposeToolbarView {
button.layer.cornerCurve = .continuous button.layer.cornerCurve = .continuous
} }
private func updateToolbarButtonUserInterfaceStyle() {
switch traitCollection.userInterfaceStyle {
case .light:
mediaButton.setImage(UIImage(systemName: "photo", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
emojiButton.setImage(UIImage(systemName: "face.smiling", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
case .dark:
mediaButton.setImage(UIImage(systemName: "photo.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
emojiButton.setImage(UIImage(systemName: "face.smiling.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
contentWarningButton.setImage(UIImage(systemName: "exclamationmark.shield.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular))!, for: .normal)
default:
assertionFailure()
}
visibilityButton.menu = createVisibilityContextMenu(interfaceStyle: traitCollection.userInterfaceStyle)
}
private func createMediaContextMenu() -> UIMenu { private func createMediaContextMenu() -> UIMenu {
var children: [UIMenuElement] = [] var children: [UIMenuElement] = []
let photoLibraryAction = UIAction(title: L10n.Scene.Compose.MediaSelection.photoLibrary, image: UIImage(systemName: "rectangle.on.rectangle"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in let photoLibraryAction = UIAction(title: L10n.Scene.Compose.MediaSelection.photoLibrary, image: UIImage(systemName: "rectangle.on.rectangle"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in
@ -208,9 +246,9 @@ extension ComposeToolbarView {
return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children) return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: children)
} }
private func createVisibilityContextMenu() -> UIMenu { private func createVisibilityContextMenu(interfaceStyle: UIUserInterfaceStyle) -> UIMenu {
let children: [UIMenuElement] = VisibilitySelectionType.allCases.map { type in let children: [UIMenuElement] = VisibilitySelectionType.allCases.map { type in
UIAction(title: type.title, image: type.image, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in UIAction(title: type.title, image: type.image(interfaceStyle: interfaceStyle), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in
guard let self = self else { return } guard let self = self else { return }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: visibilitySelectionType: %s", ((#file as NSString).lastPathComponent), #line, #function, type.rawValue) os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: visibilitySelectionType: %s", ((#file as NSString).lastPathComponent), #line, #function, type.rawValue)
self.delegate?.composeToolbarView(self, visibilityButtonDidPressed: self.visibilityButton, visibilitySelectionType: type) self.delegate?.composeToolbarView(self, visibilityButtonDidPressed: self.visibilityButton, visibilitySelectionType: type)

View File

@ -16,14 +16,14 @@ final class WelcomeIllustrationView: UIView {
let leftHillImageView = UIImageView() let leftHillImageView = UIImageView()
let centerHillImageView = UIImageView() let centerHillImageView = UIImageView()
private let cloudBaseImage = Asset.Welcome.Illustration.cloudBase.image private let cloudBaseImage = Asset.Scene.Welcome.Illustration.cloudBase.image
private let elephantThreeOnGrassWithTreeTwoImage = Asset.Welcome.Illustration.elephantThreeOnGrassWithTreeTwo.image private let elephantThreeOnGrassWithTreeTwoImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrassWithTreeTwo.image
private let elephantThreeOnGrassWithTreeThreeImage = Asset.Welcome.Illustration.elephantThreeOnGrassWithTreeThree.image private let elephantThreeOnGrassWithTreeThreeImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrassWithTreeThree.image
private let elephantThreeOnGrassImage = Asset.Welcome.Illustration.elephantThreeOnGrass.image private let elephantThreeOnGrassImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrass.image
// layout outside // layout outside
let elephantOnAirplaneWithContrailImageView: UIImageView = { let elephantOnAirplaneWithContrailImageView: UIImageView = {
let imageView = UIImageView(image: Asset.Welcome.Illustration.elephantOnAirplaneWithContrail.image) let imageView = UIImageView(image: Asset.Scene.Welcome.Illustration.elephantOnAirplaneWithContrail.image)
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFill
return imageView return imageView
}() }()
@ -43,7 +43,7 @@ final class WelcomeIllustrationView: UIView {
extension WelcomeIllustrationView { extension WelcomeIllustrationView {
private func _init() { private func _init() {
backgroundColor = Asset.Welcome.Illustration.backgroundCyan.color backgroundColor = Asset.Scene.Welcome.Illustration.backgroundCyan.color
let topPaddingView = UIView() let topPaddingView = UIView()

View File

@ -17,7 +17,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint? var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint?
private(set) lazy var logoImageView: UIImageView = { private(set) lazy var logoImageView: UIImageView = {
let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Welcome.mastodonLogo.image : Asset.Welcome.mastodonLogoBlackLarge.image let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Scene.Welcome.mastodonLogo.image : Asset.Scene.Welcome.mastodonLogoBlackLarge.image
let imageView = UIImageView(image: image) let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView return imageView

View File

@ -100,7 +100,7 @@ final class ProfileHeaderView: UIView {
label.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular)) label.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
label.adjustsFontSizeToFitWidth = true label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5 label.minimumScaleFactor = 0.5
label.textColor = Asset.Profile.Banner.usernameGray.color label.textColor = Asset.Scene.Profile.Banner.usernameGray.color
label.text = "@alice" label.text = "@alice"
label.applyShadow(color: UIColor.black.withAlphaComponent(0.2), alpha: 0.5, x: 0, y: 2, blur: 2, spread: 0) label.applyShadow(color: UIColor.black.withAlphaComponent(0.2), alpha: 0.5, x: 0, y: 2, blur: 2, spread: 0)
return label return label
@ -131,7 +131,7 @@ final class ProfileHeaderView: UIView {
textEditorView.scrollView.isScrollEnabled = false textEditorView.scrollView.isScrollEnabled = false
textEditorView.isScrollEnabled = false textEditorView.isScrollEnabled = false
textEditorView.font = .preferredFont(forTextStyle: .body) textEditorView.font = .preferredFont(forTextStyle: .body)
textEditorView.backgroundColor = Asset.Profile.Banner.bioEditBackgroundGray.color textEditorView.backgroundColor = Asset.Scene.Profile.Banner.bioEditBackgroundGray.color
textEditorView.layer.masksToBounds = true textEditorView.layer.masksToBounds = true
textEditorView.layer.cornerCurve = .continuous textEditorView.layer.cornerCurve = .continuous
textEditorView.layer.cornerRadius = 10 textEditorView.layer.cornerRadius = 10
@ -356,9 +356,9 @@ extension ProfileHeaderView {
bioTextEditorView.backgroundColor = .clear bioTextEditorView.backgroundColor = .clear
animator.addAnimations { animator.addAnimations {
self.bannerImageViewOverlayView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundEditingColor self.bannerImageViewOverlayView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundEditingColor
self.nameTextFieldBackgroundView.backgroundColor = Asset.Profile.Banner.nameEditBackgroundGray.color self.nameTextFieldBackgroundView.backgroundColor = Asset.Scene.Profile.Banner.nameEditBackgroundGray.color
self.editAvatarBackgroundView.alpha = 1 self.editAvatarBackgroundView.alpha = 1
self.bioTextEditorView.backgroundColor = Asset.Profile.Banner.bioEditBackgroundGray.color self.bioTextEditorView.backgroundColor = Asset.Scene.Profile.Banner.bioEditBackgroundGray.color
} }
} }

View File

@ -29,7 +29,7 @@ final class StatusView: UIView {
static let avatarToLabelSpacing: CGFloat = 5 static let avatarToLabelSpacing: CGFloat = 5
static let contentWarningBlurRadius: CGFloat = 12 static let contentWarningBlurRadius: CGFloat = 12
static let boostIconImage: UIImage = { static let reblogIconImage: UIImage = {
let font = UIFont.systemFont(ofSize: 13, weight: .medium) let font = UIFont.systemFont(ofSize: 13, weight: .medium)
let configuration = UIImage.SymbolConfiguration(font: font) let configuration = UIImage.SymbolConfiguration(font: font)
let image = UIImage(systemName: "arrow.2.squarepath", withConfiguration: configuration)!.withTintColor(Asset.Colors.Label.secondary.color) let image = UIImage(systemName: "arrow.2.squarepath", withConfiguration: configuration)!.withTintColor(Asset.Colors.Label.secondary.color)
@ -61,7 +61,7 @@ final class StatusView: UIView {
let headerIconLabel: UILabel = { let headerIconLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.attributedText = StatusView.iconAttributedString(image: StatusView.boostIconImage) label.attributedText = StatusView.iconAttributedString(image: StatusView.reblogIconImage)
return label return label
}() }()