Show avatar in tab/side bar when using new API
This commit is contained in:
parent
cb32c66a59
commit
7c7af945e4
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,17 +383,23 @@ 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)
|
||||||
|
config.imageProperties.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * MainSidebarMyProfileCollectionViewCell.avatarImageSize
|
||||||
|
|
||||||
|
if UIDevice.current.userInterfaceIdiom != .mac {
|
||||||
item.accessories = [
|
item.accessories = [
|
||||||
.customView(configuration: .init(customView: indicator, placement: .trailing()))
|
.customView(configuration: .init(customView: fastAccountSwitcherIndicator, placement: .trailing()))
|
||||||
]
|
]
|
||||||
item.contentConfiguration = MyProfileContentConfiguration(wrapped: item.contentConfiguration, view: $myProfileCell) { [unowned self] in
|
item.contentConfiguration = MyProfileContentConfiguration(wrapped: config, view: $myProfileCell) { [unowned self] in
|
||||||
$0.addGestureRecognizer(self.fastAccountSwitcher.createSwitcherGesture())
|
$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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue