feat: handle profile banner preview

This commit is contained in:
CMK 2021-04-28 20:36:10 +08:00
parent acbbafb18f
commit 7a13b39c51
8 changed files with 93 additions and 6 deletions

View File

@ -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

View File

@ -106,7 +106,7 @@ extension MediaPreviewViewController {
mosaicImageViewContainer.setImageViews(alpha: 1)
mosaicImageViewContainer.setImageView(alpha: 0, index: index)
}
case .profileAvatar:
case .profileAvatar, .profileBanner:
break
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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
}
}
}