feat: handle profile banner preview
This commit is contained in:
parent
acbbafb18f
commit
7a13b39c51
|
@ -164,7 +164,7 @@ struct AvatarConfigurableViewConfiguration {
|
|||
placeholderImage: UIImage? = nil,
|
||||
borderColor: UIColor? = nil,
|
||||
borderWidth: CGFloat? = nil,
|
||||
keepImageCorner: Bool = true
|
||||
keepImageCorner: Bool = false // default clip corner on image
|
||||
) {
|
||||
self.avatarImageURL = avatarImageURL
|
||||
self.placeholderImage = placeholderImage
|
||||
|
|
|
@ -106,7 +106,7 @@ extension MediaPreviewViewController {
|
|||
mosaicImageViewContainer.setImageViews(alpha: 1)
|
||||
mosaicImageViewContainer.setImageView(alpha: 0, index: index)
|
||||
}
|
||||
case .profileAvatar:
|
||||
case .profileAvatar, .profileBanner:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,26 @@ final class MediaPreviewViewModel: NSObject {
|
|||
super.init()
|
||||
}
|
||||
|
||||
init(context: AppContext, meta: ProfileBannerImagePreviewMeta, pushTransitionItem: MediaPreviewTransitionItem) {
|
||||
self.context = context
|
||||
self.initialItem = .profileBanner(meta)
|
||||
var viewControllers: [UIViewController] = []
|
||||
let managedObjectContext = self.context.managedObjectContext
|
||||
managedObjectContext.performAndWait {
|
||||
let account = managedObjectContext.object(with: meta.accountObjectID) as! MastodonUser
|
||||
let avatarURL = account.headerImageURL() ?? URL(string: "https://example.com")! // assert URL exist
|
||||
let meta = MediaPreviewImageViewModel.RemoteImagePreviewMeta(url: avatarURL, thumbnail: meta.preloadThumbnailImage)
|
||||
let mediaPreviewImageModel = MediaPreviewImageViewModel(meta: meta)
|
||||
let mediaPreviewImageViewController = MediaPreviewImageViewController()
|
||||
mediaPreviewImageViewController.viewModel = mediaPreviewImageModel
|
||||
viewControllers.append(mediaPreviewImageViewController)
|
||||
}
|
||||
self.viewControllers = viewControllers
|
||||
self.currentPage = CurrentValueSubject(0)
|
||||
self.pushTransitionItem = pushTransitionItem
|
||||
super.init()
|
||||
}
|
||||
|
||||
init(context: AppContext, meta: ProfileAvatarImagePreviewMeta, pushTransitionItem: MediaPreviewTransitionItem) {
|
||||
self.context = context
|
||||
self.initialItem = .profileAvatar(meta)
|
||||
|
@ -79,6 +99,7 @@ extension MediaPreviewViewModel {
|
|||
enum PreviewItem {
|
||||
case status(StatusImagePreviewMeta)
|
||||
case profileAvatar(ProfileAvatarImagePreviewMeta)
|
||||
case profileBanner(ProfileBannerImagePreviewMeta)
|
||||
case local(LocalImagePreviewMeta)
|
||||
}
|
||||
|
||||
|
@ -93,6 +114,11 @@ extension MediaPreviewViewModel {
|
|||
let preloadThumbnailImage: UIImage?
|
||||
}
|
||||
|
||||
struct ProfileBannerImagePreviewMeta {
|
||||
let accountObjectID: NSManagedObjectID
|
||||
let preloadThumbnailImage: UIImage?
|
||||
}
|
||||
|
||||
struct LocalImagePreviewMeta {
|
||||
let image: UIImage
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import TwitterTextEditor
|
|||
|
||||
protocol ProfileHeaderViewDelegate: AnyObject {
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, avatarImageViewDidPressed imageView: UIImageView)
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, bannerImageViewDidPressed imageView: UIImageView)
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, relationshipButtonDidPressed button: ProfileRelationshipActionButton)
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, activeLabel: ActiveLabel, entityDidPressed entity: ActiveEntity)
|
||||
|
||||
|
@ -43,6 +44,7 @@ final class ProfileHeaderView: UIView {
|
|||
imageView.image = .placeholder(color: ProfileHeaderView.bannerImageViewPlaceholderColor)
|
||||
imageView.backgroundColor = ProfileHeaderView.bannerImageViewPlaceholderColor
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.isUserInteractionEnabled = true
|
||||
// #if DEBUG
|
||||
// imageView.image = .placeholder(color: .red)
|
||||
// #endif
|
||||
|
@ -338,6 +340,11 @@ extension ProfileHeaderView {
|
|||
let avatarImageViewSingleTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||
avatarImageView.addGestureRecognizer(avatarImageViewSingleTapGestureRecognizer)
|
||||
avatarImageViewSingleTapGestureRecognizer.addTarget(self, action: #selector(ProfileHeaderView.avatarImageViewDidPressed(_:)))
|
||||
|
||||
let bannerImageViewSingleTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||
bannerImageView.addGestureRecognizer(bannerImageViewSingleTapGestureRecognizer)
|
||||
bannerImageViewSingleTapGestureRecognizer.addTarget(self, action: #selector(ProfileHeaderView.bannerImageViewDidPressed(_:)))
|
||||
|
||||
relationshipActionButton.addTarget(self, action: #selector(ProfileHeaderView.relationshipActionButtonDidPressed(_:)), for: .touchUpInside)
|
||||
|
||||
configure(state: .normal)
|
||||
|
@ -402,6 +409,11 @@ extension ProfileHeaderView {
|
|||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.profileHeaderView(self, avatarImageViewDidPressed: avatarImageView)
|
||||
}
|
||||
|
||||
@objc private func bannerImageViewDidPressed(_ sender: UITapGestureRecognizer) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.profileHeaderView(self, bannerImageViewDidPressed: bannerImageView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ActiveLabelDelegate
|
||||
|
|
|
@ -679,6 +679,37 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, bannerImageViewDidPressed imageView: UIImageView) {
|
||||
guard let mastodonUser = viewModel.mastodonUser.value else { return }
|
||||
guard let header = imageView.image else { return }
|
||||
|
||||
let meta = MediaPreviewViewModel.ProfileBannerImagePreviewMeta(
|
||||
accountObjectID: mastodonUser.objectID,
|
||||
preloadThumbnailImage: header
|
||||
)
|
||||
let pushTransitionItem = MediaPreviewTransitionItem(
|
||||
source: .profileBanner(profileHeaderView),
|
||||
previewableViewController: self
|
||||
)
|
||||
pushTransitionItem.aspectRatio = header.size
|
||||
pushTransitionItem.sourceImageView = imageView
|
||||
pushTransitionItem.initialFrame = {
|
||||
let initialFrame = imageView.superview!.convert(imageView.frame, to: nil)
|
||||
assert(initialFrame != .zero)
|
||||
return initialFrame
|
||||
}()
|
||||
pushTransitionItem.image = header
|
||||
|
||||
let mediaPreviewViewModel = MediaPreviewViewModel(
|
||||
context: context,
|
||||
meta: meta,
|
||||
pushTransitionItem: pushTransitionItem
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
self.coordinator.present(scene: .mediaPreview(viewModel: mediaPreviewViewModel), from: self, transition: .custom(transitioningDelegate: self.mediaPreviewTransitionController))
|
||||
}
|
||||
}
|
||||
|
||||
func profileHeaderView(_ profileHeaderView: ProfileHeaderView, relationshipButtonDidPressed button: ProfileRelationshipActionButton) {
|
||||
let relationshipActionSet = viewModel.relationshipActionOptionSet.value
|
||||
if relationshipActionSet.contains(.edit) {
|
||||
|
|
|
@ -204,7 +204,7 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
|||
transitionItem.imageView = imageView
|
||||
transitionItem.snapshotTransitioning = snapshot
|
||||
transitionItem.initialFrame = snapshot.frame
|
||||
transitionItem.targetFrame = targetFrame
|
||||
transitionItem.targetFrame = targetFrame ?? snapshot.frame
|
||||
|
||||
// disable interaction
|
||||
fromVC.pagingViewConttroller.isUserInteractionEnabled = false
|
||||
|
@ -215,6 +215,12 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
|||
self.transitionItem.snapshotRaw?.alpha = 0.0
|
||||
|
||||
animator.addAnimations {
|
||||
switch self.transitionItem.source {
|
||||
case .profileBanner:
|
||||
self.transitionItem.snapshotTransitioning?.alpha = 0.4
|
||||
default:
|
||||
break
|
||||
}
|
||||
fromVC.closeButtonBackground.alpha = 0
|
||||
fromVC.visualEffectView.effect = nil
|
||||
self.transitionItem.sourceImageViewCornerRadius.flatMap { self.transitionItem.snapshotTransitioning?.layer.cornerRadius = $0 }
|
||||
|
@ -310,11 +316,18 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
|
|||
|
||||
itemAnimator.addAnimations {
|
||||
if toPosition == .end {
|
||||
if let targetFrame = self.transitionItem.targetFrame {
|
||||
self.transitionItem.snapshotTransitioning?.frame = targetFrame
|
||||
} else {
|
||||
switch self.transitionItem.source {
|
||||
case .profileBanner where toPosition == .end:
|
||||
// fade transition for banner
|
||||
self.transitionItem.snapshotTransitioning?.alpha = 0
|
||||
default:
|
||||
if let targetFrame = self.transitionItem.targetFrame {
|
||||
self.transitionItem.snapshotTransitioning?.frame = targetFrame
|
||||
} else {
|
||||
self.transitionItem.snapshotTransitioning?.alpha = 0
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if let initialFrame = self.transitionItem.initialFrame {
|
||||
self.transitionItem.snapshotTransitioning?.frame = initialFrame
|
||||
|
|
|
@ -43,6 +43,7 @@ extension MediaPreviewTransitionItem {
|
|||
enum Source {
|
||||
case mosaic(MosaicImageViewContainer)
|
||||
case profileAvatar(ProfileHeaderView)
|
||||
case profileBanner(ProfileHeaderView)
|
||||
|
||||
func updateAppearance(position: UIViewAnimatingPosition, index: Int?) {
|
||||
let alpha: CGFloat = position == .end ? 1 : 0
|
||||
|
@ -55,6 +56,8 @@ extension MediaPreviewTransitionItem {
|
|||
}
|
||||
case .profileAvatar(let profileHeaderView):
|
||||
profileHeaderView.avatarImageView.alpha = alpha
|
||||
case .profileBanner:
|
||||
break // keep source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ extension MediaPreviewableViewController {
|
|||
return imageView.superview!.convert(imageView.frame, to: nil)
|
||||
case .profileAvatar(let profileHeaderView):
|
||||
return profileHeaderView.avatarImageView.superview!.convert(profileHeaderView.avatarImageView.frame, to: nil)
|
||||
case .profileBanner:
|
||||
return nil // fallback to snapshot.frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue