Show avatar in tab/side bar when using new API

This commit is contained in:
Shadowfacts 2024-08-21 16:12:05 -04:00
parent cb32c66a59
commit 7c7af945e4
2 changed files with 99 additions and 18 deletions

View File

@ -11,14 +11,14 @@ import UserAccounts
class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell { class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
private var verticalImageInset: CGFloat { static var verticalImageInset: CGFloat {
if UIDevice.current.userInterfaceIdiom == .mac { if UIDevice.current.userInterfaceIdiom == .mac {
return (28 - avatarImageSize) / 2 return (28 - avatarImageSize) / 2
} else { } else {
return (44 - avatarImageSize) / 2 return (44 - avatarImageSize) / 2
} }
} }
private var avatarImageSize: CGFloat { static var avatarImageSize: CGFloat {
if UIDevice.current.userInterfaceIdiom == .mac { if UIDevice.current.userInterfaceIdiom == .mac {
return 20 return 20
} else { } else {
@ -72,11 +72,11 @@ class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
return return
} }
config.image = image config.image = image
config.directionalLayoutMargins.top = self.verticalImageInset config.directionalLayoutMargins.top = MainSidebarMyProfileCollectionViewCell.verticalImageInset
config.directionalLayoutMargins.bottom = self.verticalImageInset config.directionalLayoutMargins.bottom = MainSidebarMyProfileCollectionViewCell.verticalImageInset
config.imageProperties.maximumSize = CGSize(width: self.avatarImageSize, height: self.avatarImageSize) config.imageProperties.maximumSize = CGSize(width: MainSidebarMyProfileCollectionViewCell.avatarImageSize, height: MainSidebarMyProfileCollectionViewCell.avatarImageSize)
config.imageProperties.reservedLayoutSize = CGSize(width: UIListContentConfiguration.ImageProperties.standardDimension, height: 0) config.imageProperties.reservedLayoutSize = CGSize(width: UIListContentConfiguration.ImageProperties.standardDimension, height: 0)
config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * self.avatarImageSize config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * MainSidebarMyProfileCollectionViewCell.avatarImageSize
self.contentConfiguration = config self.contentConfiguration = config
} }
} }
@ -86,7 +86,7 @@ class MainSidebarMyProfileCollectionViewCell: UICollectionViewListCell {
guard var config = self.contentConfiguration as? UIListContentConfiguration else { guard var config = self.contentConfiguration as? UIListContentConfiguration else {
return return
} }
config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * avatarImageSize config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * MainSidebarMyProfileCollectionViewCell.avatarImageSize
self.contentConfiguration = config self.contentConfiguration = config
} }

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Combine import Combine
import Pachyderm import Pachyderm
import TuskerPreferences
@available(iOS 18.0, *) @available(iOS 18.0, *)
class NewMainTabBarViewController: BaseMainTabBarViewController { class NewMainTabBarViewController: BaseMainTabBarViewController {
@ -52,7 +53,7 @@ class NewMainTabBarViewController: BaseMainTabBarViewController {
bookmarksTab.preferredPlacement = .optional bookmarksTab.preferredPlacement = .optional
favoritesTab = UITab(title: "Favorites", image: UIImage(systemName: "star"), identifier: Tab.favorites.rawValue, viewControllerProvider: viewControllerProvider) favoritesTab = UITab(title: "Favorites", image: UIImage(systemName: "star"), identifier: Tab.favorites.rawValue, viewControllerProvider: viewControllerProvider)
favoritesTab.preferredPlacement = .optional favoritesTab.preferredPlacement = .optional
myProfileTab = UITab(title: "My Profile", image: UIImage(systemName: "person"), identifier: Tab.myProfile.rawValue, viewControllerProvider: viewControllerProvider) myProfileTab = MyProfileTab(mastodonController: mastodonController, viewControllerProvider: viewControllerProvider)
listsGroup = UITabGroup(title: "Lists", image: nil, identifier: Tab.lists.rawValue, children: []) { _ in listsGroup = UITabGroup(title: "Lists", image: nil, identifier: Tab.lists.rawValue, children: []) { _ in
// this closure is necessary to prevent UIKit from crashing (FB14860961) // this closure is necessary to prevent UIKit from crashing (FB14860961)
@ -362,6 +363,13 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
} }
} }
private var fastAccountSwitcherIndicator: UIView = {
let indicator = FastAccountSwitcherIndicatorView()
// need to explicitly set the frame to get it vertically centered
indicator.frame = CGRect(origin: .zero, size: indicator.intrinsicContentSize)
return indicator
}()
@available(iOS 18.0, *) @available(iOS 18.0, *)
extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate { extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate {
func tabBarController(_ tabBarController: UITabBarController, sidebarVisibilityWillChange sidebar: UITabBarController.Sidebar, animator: any UITabBarController.Sidebar.Animating) { func tabBarController(_ tabBarController: UITabBarController, sidebarVisibilityWillChange sidebar: UITabBarController.Sidebar, animator: any UITabBarController.Sidebar.Animating) {
@ -375,16 +383,22 @@ extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate {
func tabBarController(_ tabBarController: UITabBarController, sidebar: UITabBarController.Sidebar, itemFor request: UITabSidebarItem.Request) -> UITabSidebarItem { func tabBarController(_ tabBarController: UITabBarController, sidebar: UITabBarController.Sidebar, itemFor request: UITabSidebarItem.Request) -> UITabSidebarItem {
let item = UITabSidebarItem(request: request) let item = UITabSidebarItem(request: request)
if case .tab(let tab) = request.content, if case .tab(let tab) = request.content,
UIDevice.current.userInterfaceIdiom != .mac, tab.identifier == Tab.myProfile.rawValue,
tab.identifier == Tab.myProfile.rawValue { var config = item.contentConfiguration as? UIListContentConfiguration {
let indicator = FastAccountSwitcherIndicatorView() config.directionalLayoutMargins.top = MainSidebarMyProfileCollectionViewCell.verticalImageInset
// need to explicitly set the frame to get it vertically centered config.directionalLayoutMargins.bottom = MainSidebarMyProfileCollectionViewCell.verticalImageInset
indicator.frame = CGRect(origin: .zero, size: indicator.intrinsicContentSize) config.imageProperties.maximumSize = CGSize(width: MainSidebarMyProfileCollectionViewCell.avatarImageSize, height: MainSidebarMyProfileCollectionViewCell.avatarImageSize)
item.accessories = [ config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * MainSidebarMyProfileCollectionViewCell.avatarImageSize
.customView(configuration: .init(customView: indicator, placement: .trailing()))
] if UIDevice.current.userInterfaceIdiom != .mac {
item.contentConfiguration = MyProfileContentConfiguration(wrapped: item.contentConfiguration, view: $myProfileCell) { [unowned self] in item.accessories = [
$0.addGestureRecognizer(self.fastAccountSwitcher.createSwitcherGesture()) .customView(configuration: .init(customView: fastAccountSwitcherIndicator, placement: .trailing()))
]
item.contentConfiguration = MyProfileContentConfiguration(wrapped: config, view: $myProfileCell) { [unowned self] in
$0.addGestureRecognizer(self.fastAccountSwitcher.createSwitcherGesture())
}
} else {
item.contentConfiguration = config
} }
} }
return item return item
@ -502,3 +516,70 @@ private struct MyProfileContentConfiguration: UIContentConfiguration {
return .init(wrapped: wrapped.updated(for: state), view: $view, configureView: configureView) return .init(wrapped: wrapped.updated(for: state), view: $view, configureView: configureView)
} }
} }
@available(iOS 18.0, *)
private class MyProfileTab: UITab {
private let mastodonController: MastodonController
private var avatarStyle: AvatarStyle?
init(mastodonController: MastodonController, viewControllerProvider: @escaping (UITab) -> UIViewController) {
self.mastodonController = mastodonController
// try to add the avatar image synchronously if possible
var avatarImage: UIImage?
if !Preferences.shared.grayscaleImages,
let account = mastodonController.account,
let avatarURL = account.avatar,
let avatar = ImageCache.avatars.get(avatarURL) {
avatarImage = Self.renderAvatar(avatar.image)
self.avatarStyle = Preferences.shared.avatarStyle
}
let image = avatarImage ?? UIImage(systemName: "person")!
super.init(title: "My Profile", image: image, identifier: NewMainTabBarViewController.Tab.myProfile.rawValue, viewControllerProvider: viewControllerProvider)
if avatarImage == nil {
Task {
await updateAvatar()
}
}
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
}
private func updateAvatar() async {
guard let account = try? await mastodonController.getOwnAccount(),
let avatarURL = account.avatar,
let image = await ImageCache.avatars.get(avatarURL).1 else {
return
}
let maybeGrayscale = await ImageGrayscalifier.convertIfNecessary(url: avatarURL, image: image) ?? image
let rendered = Self.renderAvatar(maybeGrayscale)
self.avatarStyle = Preferences.shared.avatarStyle
self.image = rendered
}
private static func renderAvatar(_ image: UIImage) -> UIImage {
let size = MainSidebarMyProfileCollectionViewCell.avatarImageSize
let radius = Preferences.shared.avatarStyle.cornerRadiusFraction * size
let rect = CGRect(x: 0, y: 0, width: size, height: size)
let renderer = UIGraphicsImageRenderer(bounds: rect)
let rendered = renderer.image { ctx in
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
image.draw(in: rect)
}
return rendered.withRenderingMode(.alwaysOriginal)
}
@objc private func preferencesChanged() {
if avatarStyle != nil,
avatarStyle != Preferences.shared.avatarStyle {
Task {
await updateAvatar()
}
}
}
}