diff --git a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift index 2088df72f..ff408d54e 100644 --- a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift +++ b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift @@ -371,7 +371,7 @@ extension ProfileHeaderViewController { } else if bannerContainerBottomOffset < containerSafeAreaInset.top { // 3 // banner bottom pin to navigation bar bottom and - // the `progress` growth to 1 then segemented control pin to top + // the `progress` growth to 1 then segmented control pin to top bannerImageView.frame.origin.y = -containerSafeAreaInset.top let bannerImageHeight = bannerContainerInWindow.size.height + containerSafeAreaInset.top + (containerSafeAreaInset.top - bannerContainerBottomOffset) bannerImageView.frame.size.height = bannerImageHeight @@ -406,7 +406,7 @@ extension ProfileHeaderViewController { setProfileBannerFade(alpha: 1) } } - + private func setProfileBannerFade(alpha: CGFloat) { profileHeaderView.avatarImageViewBackgroundView.alpha = alpha profileHeaderView.avatarImageView.alpha = alpha diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift index 2b938bac9..f9f2e98d4 100644 --- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift +++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift @@ -50,8 +50,12 @@ final class ProfileHeaderView: UIView { imageView.accessibilityIgnoresInvertColors = true return imageView }() - let bannerImageViewOverlayView: UIView = { - let overlayView = UIView() + + // known issue: + // in iOS 14 blur maybe disappear when banner image moving and scaling + static let bannerImageViewOverlayBlurEffect = UIBlurEffect(style: .systemMaterialDark) + let bannerImageViewOverlayVisualEffectView: UIVisualEffectView = { + let overlayView = UIVisualEffectView(effect: nil) overlayView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundNormalColor return overlayView }() @@ -79,6 +83,9 @@ final class ProfileHeaderView: UIView { editAvatarBackgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.6) editAvatarButton.tintColor = .white } + + static let avatarImageViewOverlayBlurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) + let avatarImageViewOverlayVisualEffectView = UIVisualEffectView(effect: nil) let editAvatarBackgroundView: UIView = { let view = UIView() @@ -226,13 +233,13 @@ extension ProfileHeaderView { bannerImageView.frame = bannerContainerView.bounds bannerContainerView.addSubview(bannerImageView) - bannerImageViewOverlayView.translatesAutoresizingMaskIntoConstraints = false - bannerImageView.addSubview(bannerImageViewOverlayView) + bannerImageViewOverlayVisualEffectView.translatesAutoresizingMaskIntoConstraints = false + bannerImageView.addSubview(bannerImageViewOverlayVisualEffectView) NSLayoutConstraint.activate([ - bannerImageViewOverlayView.topAnchor.constraint(equalTo: bannerImageView.topAnchor), - bannerImageViewOverlayView.leadingAnchor.constraint(equalTo: bannerImageView.leadingAnchor), - bannerImageViewOverlayView.trailingAnchor.constraint(equalTo: bannerImageView.trailingAnchor), - bannerImageViewOverlayView.bottomAnchor.constraint(equalTo: bannerImageView.bottomAnchor), + bannerImageViewOverlayVisualEffectView.topAnchor.constraint(equalTo: bannerImageView.topAnchor), + bannerImageViewOverlayVisualEffectView.leadingAnchor.constraint(equalTo: bannerImageView.leadingAnchor), + bannerImageViewOverlayVisualEffectView.trailingAnchor.constraint(equalTo: bannerImageView.trailingAnchor), + bannerImageViewOverlayVisualEffectView.bottomAnchor.constraint(equalTo: bannerImageView.bottomAnchor), ]) // avatar @@ -253,6 +260,15 @@ extension ProfileHeaderView { avatarImageView.widthAnchor.constraint(equalToConstant: ProfileHeaderView.avatarImageViewSize.width).priority(.required - 1), avatarImageView.heightAnchor.constraint(equalToConstant: ProfileHeaderView.avatarImageViewSize.height).priority(.required - 1), ]) + + avatarImageViewOverlayVisualEffectView.translatesAutoresizingMaskIntoConstraints = false + avatarImageViewBackgroundView.addSubview(avatarImageViewOverlayVisualEffectView) + NSLayoutConstraint.activate([ + avatarImageViewOverlayVisualEffectView.topAnchor.constraint(equalTo: avatarImageViewBackgroundView.topAnchor), + avatarImageViewOverlayVisualEffectView.leadingAnchor.constraint(equalTo: avatarImageViewBackgroundView.leadingAnchor), + avatarImageViewOverlayVisualEffectView.trailingAnchor.constraint(equalTo: avatarImageViewBackgroundView.trailingAnchor), + avatarImageViewOverlayVisualEffectView.bottomAnchor.constraint(equalTo: avatarImageViewBackgroundView.bottomAnchor), + ]) editAvatarBackgroundView.translatesAutoresizingMaskIntoConstraints = false avatarImageView.addSubview(editAvatarBackgroundView) @@ -425,7 +441,7 @@ extension ProfileHeaderView { bioTextEditorView.isHidden = true animator.addAnimations { - self.bannerImageViewOverlayView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundNormalColor + self.bannerImageViewOverlayVisualEffectView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundNormalColor self.nameTextFieldBackgroundView.backgroundColor = .clear self.editAvatarBackgroundView.alpha = 0 } @@ -441,7 +457,7 @@ extension ProfileHeaderView { editAvatarBackgroundView.alpha = 0 bioTextEditorView.backgroundColor = .clear animator.addAnimations { - self.bannerImageViewOverlayView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundEditingColor + self.bannerImageViewOverlayVisualEffectView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundEditingColor self.nameTextFieldBackgroundView.backgroundColor = Asset.Scene.Profile.Banner.nameEditBackgroundGray.color self.editAvatarBackgroundView.alpha = 1 self.bioTextEditorView.backgroundColor = Asset.Scene.Profile.Banner.bioEditBackgroundGray.color diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 57b677a71..fd14f9830 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -527,7 +527,18 @@ extension ProfileViewController { self.profileSegmentedViewController.pagingViewController.isScrollEnabled = needsPaingEnabled } .store(in: &disposeBag) - + viewModel.needsImageOverlayBlurred + .receive(on: RunLoop.main) + .sink { [weak self] needsImageOverlayBlurred in + guard let self = self else { return } + UIView.animate(withDuration: 0.33) { + let bannerEffect: UIVisualEffect? = needsImageOverlayBlurred ? ProfileHeaderView.bannerImageViewOverlayBlurEffect : nil + self.profileHeaderViewController.profileHeaderView.bannerImageViewOverlayVisualEffectView.effect = bannerEffect + let avatarEffect: UIVisualEffect? = needsImageOverlayBlurred ? ProfileHeaderView.avatarImageViewOverlayBlurEffect : nil + self.profileHeaderViewController.profileHeaderView.avatarImageViewOverlayVisualEffectView.effect = avatarEffect + } + } + .store(in: &disposeBag) profileHeaderViewController.profileHeaderView.delegate = self } diff --git a/Mastodon/Scene/Profile/ProfileViewModel.swift b/Mastodon/Scene/Profile/ProfileViewModel.swift index 2d4bc429c..223a12215 100644 --- a/Mastodon/Scene/Profile/ProfileViewModel.swift +++ b/Mastodon/Scene/Profile/ProfileViewModel.swift @@ -61,6 +61,7 @@ class ProfileViewModel: NSObject { let needsPagePinToTop = CurrentValueSubject(false) let needsPaingEnabled = CurrentValueSubject(true) + let needsImageOverlayBlurred = CurrentValueSubject(false) init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) { self.context = context @@ -148,13 +149,22 @@ class ProfileViewModel: NSObject { } .store(in: &disposeBag) - Publishers.CombineLatest( + let isBlockingOrBlocked = Publishers.CombineLatest( isBlocking, isBlockedBy ) - .map { !($0 || $1) } - .assign(to: \.value, on: needsPaingEnabled) - .store(in: &disposeBag) + .map { $0 || $1 } + .share() + + isBlockingOrBlocked + .map { !$0 } + .assign(to: \.value, on: needsPaingEnabled) + .store(in: &disposeBag) + + isBlockingOrBlocked + .map { $0 } + .assign(to: \.value, on: needsImageOverlayBlurred) + .store(in: &disposeBag) setup() }