Rework cvc wallpaper blur again.
This commit is contained in:
parent
a61098bcda
commit
92c841caa9
@ -84,6 +84,7 @@
|
||||
341D392925472F3B00996E7B /* CVViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341D392825472F3B00996E7B /* CVViewState.swift */; };
|
||||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; };
|
||||
3420CF6D26327B1A00E7DCB0 /* CVBackgroundContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3420CF6C26327B1A00E7DCB0 /* CVBackgroundContainer.swift */; };
|
||||
3420CF6F2638A01A00E7DCB0 /* CVWallpaperBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */; };
|
||||
3421981C21061D2E00C57195 /* ByteParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3421981B21061D2E00C57195 /* ByteParserTest.swift */; };
|
||||
34235F3824213550008C74CB /* ConversationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34235F3724213550008C74CB /* ConversationSettingsViewController.swift */; };
|
||||
3425A5992631DC05006D5863 /* LinkPreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3425A5982631DC05006D5863 /* LinkPreviewState.swift */; };
|
||||
@ -1096,6 +1097,7 @@
|
||||
341F2C0D1F2B8AE700D07D6B /* DebugUIMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMisc.h; sourceTree = "<group>"; };
|
||||
341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = "<group>"; };
|
||||
3420CF6C26327B1A00E7DCB0 /* CVBackgroundContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CVBackgroundContainer.swift; sourceTree = "<group>"; };
|
||||
3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CVWallpaperBlurView.swift; sourceTree = "<group>"; };
|
||||
3421981B21061D2E00C57195 /* ByteParserTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ByteParserTest.swift; sourceTree = "<group>"; };
|
||||
34235F3724213550008C74CB /* ConversationSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
3425A5982631DC05006D5863 /* LinkPreviewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkPreviewState.swift; sourceTree = "<group>"; };
|
||||
@ -2694,6 +2696,7 @@
|
||||
347C381C252CE69400F3D941 /* CVStackView.swift */,
|
||||
3470C8812559B23F00F5847C /* CVUpdate.swift */,
|
||||
345772B5257E4E01001D7D44 /* CVViewStateSnapshot.swift */,
|
||||
3420CF6E2638A01A00E7DCB0 /* CVWallpaperBlurView.swift */,
|
||||
);
|
||||
path = CV;
|
||||
sourceTree = "<group>";
|
||||
@ -5287,6 +5290,7 @@
|
||||
34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */,
|
||||
886BB3D325BA0CA400079781 /* SetWallpaperViewController.swift in Sources */,
|
||||
887CD47D2473051D00FDD265 /* DeviceTransferService+Manifest.swift in Sources */,
|
||||
3420CF6F2638A01A00E7DCB0 /* CVWallpaperBlurView.swift in Sources */,
|
||||
88D23D2623CEC0C700B0E74B /* CallUIAdapter.swift in Sources */,
|
||||
34FB6A5325D2D10400E599B1 /* PaymentsViewUtils.swift in Sources */,
|
||||
E44AD4E624E98F440035D7B8 /* PhotoCaptureDismiss.swift in Sources */,
|
||||
|
||||
@ -174,24 +174,6 @@ public class CVCell: UICollectionViewCell, CVItemCell, CVRootComponentHost {
|
||||
renderItem: renderItem,
|
||||
messageSwipeActionState: messageSwipeActionState)
|
||||
}
|
||||
|
||||
public func willSnapshotForMessageActions() {
|
||||
guard let rootComponent = rootComponent,
|
||||
let componentView = componentView else {
|
||||
owsFailDebug("Missing component.")
|
||||
return
|
||||
}
|
||||
rootComponent.willSnapshotForMessageActions(componentView: componentView)
|
||||
}
|
||||
|
||||
public func didSnapshotForMessageActions() {
|
||||
guard let rootComponent = rootComponent,
|
||||
let componentView = componentView else {
|
||||
owsFailDebug("Missing component.")
|
||||
return
|
||||
}
|
||||
rootComponent.didSnapshotForMessageActions(componentView: componentView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -401,12 +383,12 @@ public extension CVRootComponentHost {
|
||||
componentView: componentView)
|
||||
}
|
||||
|
||||
func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) {
|
||||
func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate) {
|
||||
guard let rootComponent = rootComponent,
|
||||
let componentView = componentView else {
|
||||
owsFailDebug("Missing component.")
|
||||
return
|
||||
}
|
||||
rootComponent.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView)
|
||||
rootComponent.updateWallpaperBlur(delegate: delegate, componentView: componentView)
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,9 +55,6 @@ public protocol CVComponent: class {
|
||||
func cellDidBecomeVisible(componentView: CVComponentView,
|
||||
renderItem: CVRenderItem,
|
||||
messageSwipeActionState: CVMessageSwipeActionState)
|
||||
|
||||
func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -77,9 +74,8 @@ public protocol CVRootComponent: CVComponent {
|
||||
|
||||
var isDedicatedCell: Bool { get }
|
||||
|
||||
func willSnapshotForMessageActions(componentView: CVComponentView)
|
||||
|
||||
func didSnapshotForMessageActions(componentView: CVComponentView)
|
||||
func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -161,14 +157,6 @@ public class CVComponentBase: NSObject {
|
||||
itemModel.itemViewState.isShowingSelectionUI
|
||||
}
|
||||
|
||||
public func willSnapshotForMessageActions(componentView: CVComponentView) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public func didSnapshotForMessageActions(componentView: CVComponentView) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// MARK: - Root Components
|
||||
|
||||
public static func configureCellRootComponent(rootComponent: CVRootComponent,
|
||||
@ -194,8 +182,8 @@ public class CVComponentBase: NSObject {
|
||||
|
||||
// MARK: -
|
||||
|
||||
public func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView) {
|
||||
public func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,15 +41,16 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent {
|
||||
CVComponentViewDateHeader()
|
||||
}
|
||||
|
||||
public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView) {
|
||||
super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView)
|
||||
public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView) {
|
||||
super.updateWallpaperBlur(delegate: delegate,
|
||||
componentView: componentView)
|
||||
|
||||
guard let componentView = componentView as? CVComponentViewDateHeader else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
wallpaperMaskBuilder.append(blurView: componentView.blurView)
|
||||
componentView.wallpaperBlurView?.configure(delegate: delegate)
|
||||
}
|
||||
|
||||
public func configureForRendering(componentView: CVComponentView,
|
||||
@ -87,17 +88,20 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent {
|
||||
innerStack.reset()
|
||||
outerStack.reset()
|
||||
titleLabel.removeFromSuperview()
|
||||
componentView.blurView?.removeFromSuperview()
|
||||
componentView.blurView = nil
|
||||
componentView.wallpaperBlurView?.removeFromSuperview()
|
||||
componentView.wallpaperBlurView = nil
|
||||
|
||||
if hasWallpaper {
|
||||
let wallpaperBlurView = CVWallpaperBlurView()
|
||||
wallpaperBlurView.layer.cornerRadius = 8
|
||||
innerStack.addSubviewToFillSuperviewEdges(wallpaperBlurView)
|
||||
componentView.wallpaperBlurView = wallpaperBlurView
|
||||
}
|
||||
|
||||
innerStack.configure(config: innerStackConfig,
|
||||
cellMeasurement: cellMeasurement,
|
||||
measurementKey: Self.measurementKey_innerStack,
|
||||
subviews: [ titleLabel ])
|
||||
if hasWallpaper {
|
||||
innerStack.layer.cornerRadius = 8
|
||||
componentView.blurView = innerStack
|
||||
}
|
||||
|
||||
outerStack.configure(config: outerStackConfig,
|
||||
cellMeasurement: cellMeasurement,
|
||||
@ -176,7 +180,7 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent {
|
||||
fileprivate let innerStack = ManualStackView(name: "dateHeader.innerStackView")
|
||||
fileprivate let titleLabel = CVLabel()
|
||||
|
||||
fileprivate var blurView: UIView?
|
||||
fileprivate var wallpaperBlurView: CVWallpaperBlurView?
|
||||
|
||||
fileprivate var hasWallpaper = false
|
||||
fileprivate var isDarkThemeEnabled = false
|
||||
@ -203,8 +207,8 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent {
|
||||
|
||||
titleLabel.removeFromSuperview()
|
||||
|
||||
blurView?.removeFromSuperview()
|
||||
blurView = nil
|
||||
wallpaperBlurView?.removeFromSuperview()
|
||||
wallpaperBlurView = nil
|
||||
|
||||
hasWallpaper = false
|
||||
isDarkThemeEnabled = false
|
||||
|
||||
@ -37,22 +37,6 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
componentView: componentView)
|
||||
}
|
||||
|
||||
public override func willSnapshotForMessageActions(componentView: CVComponentView) {
|
||||
guard let componentView = componentView as? CVComponentViewSystemMessage else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
componentView.blurView?.backgroundColor = bubbleBackgroundColor
|
||||
}
|
||||
|
||||
public override func didSnapshotForMessageActions(componentView: CVComponentView) {
|
||||
guard let componentView = componentView as? CVComponentViewSystemMessage else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
componentView.blurView?.backgroundColor = .clear
|
||||
}
|
||||
|
||||
private var bubbleBackgroundColor: UIColor {
|
||||
Theme.backgroundColor
|
||||
}
|
||||
@ -83,15 +67,16 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
layoutMargins: .zero)
|
||||
}
|
||||
|
||||
public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView) {
|
||||
super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView)
|
||||
public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView) {
|
||||
super.updateWallpaperBlur(delegate: delegate,
|
||||
componentView: componentView)
|
||||
|
||||
guard let componentView = componentView as? CVComponentViewSystemMessage else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
wallpaperMaskBuilder.append(blurView: componentView.blurView)
|
||||
componentView.wallpaperBlurView?.configure(delegate: delegate)
|
||||
}
|
||||
|
||||
public func buildComponentView(componentDelegate: CVComponentDelegate) -> CVComponentView {
|
||||
@ -212,8 +197,8 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
measurementKey: Self.measurementKey_outerHStack,
|
||||
subviews: outerHStackViews)
|
||||
|
||||
componentView.blurView?.removeFromSuperview()
|
||||
componentView.blurView = nil
|
||||
componentView.wallpaperBlurView?.removeFromSuperview()
|
||||
componentView.wallpaperBlurView = nil
|
||||
|
||||
componentView.backgroundView?.removeFromSuperview()
|
||||
componentView.backgroundView = nil
|
||||
@ -221,16 +206,15 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
let bubbleView: UIView
|
||||
|
||||
if hasWallpaper {
|
||||
// This view doesn't need to render to render blur,
|
||||
// but it does need to render for the message actions snapshot.
|
||||
let blurView = UIView.container()
|
||||
componentView.blurView = blurView
|
||||
bubbleView = blurView
|
||||
let wallpaperBlurView = CVWallpaperBlurView()
|
||||
wallpaperBlurView.layer.cornerRadius = 8
|
||||
componentView.wallpaperBlurView = wallpaperBlurView
|
||||
bubbleView = wallpaperBlurView
|
||||
} else {
|
||||
let backgroundView = UIView()
|
||||
backgroundView.backgroundColor = Theme.backgroundColor
|
||||
componentView.backgroundView = backgroundView
|
||||
bubbleView = backgroundView
|
||||
backgroundView.backgroundColor = Theme.backgroundColor
|
||||
}
|
||||
|
||||
if isFirstInCluster && isLastInCluster {
|
||||
@ -416,7 +400,7 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
fileprivate let titleLabel = CVLabel()
|
||||
fileprivate let selectionView = MessageSelectionView()
|
||||
|
||||
fileprivate var blurView: UIView?
|
||||
fileprivate var wallpaperBlurView: CVWallpaperBlurView?
|
||||
fileprivate var backgroundView: UIView?
|
||||
|
||||
fileprivate var button: OWSButton?
|
||||
@ -453,8 +437,8 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
|
||||
innerVStack.reset()
|
||||
outerVStack.reset()
|
||||
|
||||
blurView?.removeFromSuperview()
|
||||
blurView = nil
|
||||
wallpaperBlurView?.removeFromSuperview()
|
||||
wallpaperBlurView = nil
|
||||
|
||||
backgroundView?.removeFromSuperview()
|
||||
backgroundView = nil
|
||||
|
||||
@ -44,15 +44,16 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent {
|
||||
CVComponentViewThreadDetails()
|
||||
}
|
||||
|
||||
public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView) {
|
||||
super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView)
|
||||
public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView) {
|
||||
super.updateWallpaperBlur(delegate: delegate,
|
||||
componentView: componentView)
|
||||
|
||||
guard let componentView = componentView as? CVComponentViewThreadDetails else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
wallpaperMaskBuilder.append(blurView: componentView.blurView)
|
||||
componentView.wallpaperBlurView?.configure(delegate: delegate)
|
||||
}
|
||||
|
||||
public func configureForRendering(componentView componentViewParam: CVComponentView,
|
||||
@ -117,8 +118,10 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent {
|
||||
innerViews.append(UIView.spacer(withHeight: 1))
|
||||
|
||||
if conversationStyle.hasWallpaper {
|
||||
innerStackView.layer.cornerRadius = 12
|
||||
componentView.blurView = innerStackView
|
||||
let wallpaperBlurView = CVWallpaperBlurView()
|
||||
wallpaperBlurView.layer.cornerRadius = 12
|
||||
innerStackView.addSubviewToFillSuperviewEdges(wallpaperBlurView)
|
||||
componentView.wallpaperBlurView = wallpaperBlurView
|
||||
}
|
||||
|
||||
let titleLabel = componentView.titleLabel
|
||||
@ -514,7 +517,7 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent {
|
||||
fileprivate let outerStackView = ManualStackView(name: "Thread details outer")
|
||||
fileprivate let innerStackView = ManualStackView(name: "Thread details inner")
|
||||
|
||||
fileprivate var blurView: UIView?
|
||||
fileprivate var wallpaperBlurView: CVWallpaperBlurView?
|
||||
|
||||
public var isDedicatedCellView = false
|
||||
|
||||
@ -536,8 +539,8 @@ public class CVComponentThreadDetails: CVComponentBase, CVRootComponent {
|
||||
mutualGroupsLabel.text = nil
|
||||
avatarView = nil
|
||||
|
||||
blurView?.removeFromSuperview()
|
||||
blurView = nil
|
||||
wallpaperBlurView?.removeFromSuperview()
|
||||
wallpaperBlurView = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,15 +34,16 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
|
||||
CVComponentViewUnreadIndicator()
|
||||
}
|
||||
|
||||
public override func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder,
|
||||
componentView: CVComponentView) {
|
||||
super.buildWallpaperMask(wallpaperMaskBuilder, componentView: componentView)
|
||||
public override func updateWallpaperBlur(delegate: CVWallpaperBlurDelegate,
|
||||
componentView: CVComponentView) {
|
||||
super.updateWallpaperBlur(delegate: delegate,
|
||||
componentView: componentView)
|
||||
|
||||
guard let componentView = componentView as? CVComponentViewUnreadIndicator else {
|
||||
owsFailDebug("Unexpected componentView.")
|
||||
return
|
||||
}
|
||||
wallpaperMaskBuilder.append(blurView: componentView.blurView)
|
||||
componentView.wallpaperBlurView?.configure(delegate: delegate)
|
||||
}
|
||||
|
||||
public func configureForRendering(componentView: CVComponentView,
|
||||
@ -79,8 +80,8 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
|
||||
} else {
|
||||
outerStack.reset()
|
||||
titleLabel.removeFromSuperview()
|
||||
componentView.blurView?.removeFromSuperview()
|
||||
componentView.blurView = nil
|
||||
componentView.wallpaperBlurView?.removeFromSuperview()
|
||||
componentView.wallpaperBlurView = nil
|
||||
|
||||
innerStack.reset()
|
||||
innerStack.configure(config: innerStackConfig,
|
||||
@ -91,9 +92,10 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
|
||||
if hasWallpaper {
|
||||
strokeView.backgroundColor = .ows_blackAlpha80
|
||||
|
||||
let blurView = innerStack
|
||||
blurView.layer.cornerRadius = 8
|
||||
componentView.blurView = blurView
|
||||
let wallpaperBlurView = CVWallpaperBlurView()
|
||||
wallpaperBlurView.layer.cornerRadius = 8
|
||||
innerStack.addSubviewToFillSuperviewEdges(wallpaperBlurView)
|
||||
componentView.wallpaperBlurView = wallpaperBlurView
|
||||
} else {
|
||||
strokeView.backgroundColor = .ows_gray45
|
||||
}
|
||||
@ -175,7 +177,7 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
|
||||
|
||||
fileprivate let titleLabel = CVLabel()
|
||||
|
||||
fileprivate var blurView: UIView?
|
||||
fileprivate var wallpaperBlurView: CVWallpaperBlurView?
|
||||
|
||||
fileprivate var hasWallpaper = false
|
||||
fileprivate var isDarkThemeEnabled = false
|
||||
@ -201,8 +203,8 @@ public class CVComponentUnreadIndicator: CVComponentBase, CVRootComponent {
|
||||
outerStack.reset()
|
||||
innerStack.reset()
|
||||
|
||||
blurView?.removeFromSuperview()
|
||||
blurView = nil
|
||||
wallpaperBlurView?.removeFromSuperview()
|
||||
wallpaperBlurView = nil
|
||||
|
||||
hasWallpaper = false
|
||||
isDarkThemeEnabled = false
|
||||
|
||||
@ -0,0 +1,153 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol CVWallpaperBlurDelegate: class {
|
||||
var wallpaperBlurState: WallpaperBlurState? { get }
|
||||
var isAnimating: Bool { get }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class CVWallpaperBlurView: ManualLayoutView {
|
||||
|
||||
private weak var delegate: CVWallpaperBlurDelegate?
|
||||
|
||||
private let imageView = CVImageView()
|
||||
// private let imageLayer = CALayer()
|
||||
private let maskLayer = CAShapeLayer()
|
||||
|
||||
private var state: WallpaperBlurState?
|
||||
|
||||
required init() {
|
||||
super.init(name: "CVWallpaperBlurView")
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.layer.mask = maskLayer
|
||||
imageView.layer.masksToBounds = true
|
||||
addSubview(imageView)
|
||||
|
||||
// addSubview(imageView) { view in
|
||||
// guard let view = view as? CVWallpaperBlurView else {
|
||||
// owsFailDebug("Invalid view.")
|
||||
// return
|
||||
// }
|
||||
// view.imageView.frame = view.imageViewFrame
|
||||
// }
|
||||
// self.layer.addSublayer(imageLayer)
|
||||
// self.layer.masksToBounds = true
|
||||
// imageLayer.contentsGravity = .resizeAspectFill
|
||||
// imageLayer.mask = maskLayer
|
||||
// self.layer.mask = maskLayer
|
||||
|
||||
addLayoutBlock { [weak self] _ in
|
||||
self?.applyLayout()
|
||||
}
|
||||
addRedBorder()
|
||||
// addLayoutBlock { view in
|
||||
// guard let view = view as? CVWallpaperBlurView else {
|
||||
// owsFailDebug("Invalid view.")
|
||||
// return
|
||||
// }
|
||||
// view.imageLayer.frame = view.imageViewFrame
|
||||
// }
|
||||
}
|
||||
|
||||
private func applyLayout() {
|
||||
imageView.frame = imageViewFrame
|
||||
// imageLayer.frame = imageViewFrame
|
||||
|
||||
maskLayer.frame = imageView.bounds
|
||||
let maskPath = UIBezierPath(roundedRect: maskFrame, cornerRadius: layer.cornerRadius)
|
||||
maskLayer.path = maskPath.cgPath
|
||||
}
|
||||
|
||||
@available(swift, obsoleted: 1.0)
|
||||
required init(name: String) {
|
||||
owsFail("Do not use this initializer.")
|
||||
}
|
||||
|
||||
public func configure(delegate: CVWallpaperBlurDelegate) {
|
||||
// TODO: Observe provider changes.
|
||||
self.delegate = delegate
|
||||
|
||||
configure()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
guard let delegate = delegate else {
|
||||
owsFailDebug("Missing delegate.")
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
guard let state = delegate.wallpaperBlurState else {
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
guard state.id != self.state?.id else {
|
||||
ensurePositioning()
|
||||
return
|
||||
}
|
||||
self.state = state
|
||||
imageView.image = state.image
|
||||
// imageLayer.contents = state.image.cgImage
|
||||
|
||||
ensurePositioning()
|
||||
}
|
||||
|
||||
private var imageViewFrame: CGRect = .zero
|
||||
private var maskFrame: CGRect = .zero
|
||||
|
||||
private func ensurePositioning() {
|
||||
// guard let delegate = delegate else {
|
||||
// owsFailDebug("Missing delegate.")
|
||||
// resetContent()
|
||||
// return
|
||||
// }
|
||||
guard let state = self.state else {
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
// guard let image = imageView.image else {
|
||||
// owsFailDebug("Missing image.")
|
||||
// resetContent()
|
||||
// return
|
||||
// }
|
||||
let referenceView = state.referenceView
|
||||
self.imageViewFrame = self.convert(referenceView.bounds, from: referenceView)
|
||||
self.maskFrame = referenceView.convert(self.bounds, from: self)
|
||||
|
||||
// let widthFactor = referenceView.bounds.width / image.size.width
|
||||
// let heightFactor = referenceView.bounds.height / image.size.height
|
||||
// let scalingFactor = max(widthFactor, heightFactor)
|
||||
// let contentsScale = 1 / scalingFactor
|
||||
// Logger.verbose("---- scalingFactor: \(scalingFactor), contentsScale: \(layer.contentsScale), UIScreen.main.scale: \(UIScreen.main.scale)")
|
||||
//// self.layer.contentsScale
|
||||
// // https://developer.apple.com/documentation/quartzcore/calayer/1410746-contentsscale
|
||||
// // By using contentsScale to scale the blurred background image, we not only
|
||||
// // (increase) the scale so that it is rendered at the correct size, we also
|
||||
// // reduce the size of the bitmap used to present the layer content, which
|
||||
// // helps perf.
|
||||
// self.layer.contentsScale = contentsScale
|
||||
// let referencePoint = self.convert(referenceView.bounds.center, from: referenceView)
|
||||
// Logger.verbose("---- contentsCenter: \(layer.contentsCenter), position: \(layer.position), anchorPoint: \(layer.anchorPoint), referencePoint: \(referencePoint)")
|
||||
Logger.verbose("---- imageViewFrame: \(imageViewFrame)")
|
||||
Logger.verbose("---- maskFrame: \(maskFrame)")
|
||||
// self.layer.contentsCenter = CGRect(origin: .zero, size: <#T##CGSize#>) referencePoint
|
||||
// imageView.frame = imageViewFrame
|
||||
applyLayout()
|
||||
}
|
||||
|
||||
private func resetContent() {
|
||||
imageView.image = nil
|
||||
// imageLayer.contents = nil
|
||||
imageViewFrame = .zero
|
||||
maskFrame = .zero
|
||||
delegate = nil
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import Foundation
|
||||
public protocol CVBackgroundContainerDelegate: class {
|
||||
func updateSelectionHighlight()
|
||||
func updateScrollingContent()
|
||||
func updateCellWallpaperBlur()
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -19,7 +20,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer {
|
||||
case wallpaperContent = 0
|
||||
case wallpaperDimming = 1
|
||||
case selectionHighlight = 2
|
||||
case wallpaperBlur = 3
|
||||
}
|
||||
|
||||
fileprivate var wallpaperView: WallpaperView?
|
||||
@ -55,7 +55,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer {
|
||||
}
|
||||
|
||||
public func set(wallpaperView: WallpaperView?) {
|
||||
self.wallpaperView?.blurView?.removeFromSuperview()
|
||||
self.wallpaperView?.contentView?.removeFromSuperview()
|
||||
self.wallpaperView?.dimmingView?.removeFromSuperview()
|
||||
self.wallpaperView = wallpaperView
|
||||
@ -71,10 +70,6 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer {
|
||||
addSubview(dimmingView)
|
||||
dimmingView.layer.zPosition = ZPositioning.wallpaperDimming.rawValue
|
||||
}
|
||||
if let blurView = wallpaperView.blurView {
|
||||
addSubview(blurView)
|
||||
blurView.layer.zPosition = ZPositioning.wallpaperBlur.rawValue
|
||||
}
|
||||
|
||||
setNeedsLayout()
|
||||
} else {
|
||||
@ -87,17 +82,13 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer {
|
||||
|
||||
super.layoutSubviews()
|
||||
|
||||
let shouldUpdateWallpaperBlur = wallpaperView?.blurView?.frame != bounds
|
||||
let shouldUpdateSelectionHighlight = selectionHighlightView.frame != bounds
|
||||
|
||||
wallpaperView?.blurView?.frame = bounds
|
||||
wallpaperView?.contentView?.frame = bounds
|
||||
wallpaperView?.dimmingView?.frame = bounds
|
||||
selectionHighlightView.frame = bounds
|
||||
|
||||
if shouldUpdateWallpaperBlur {
|
||||
wallpaperView?.updateBlurContentAndMask()
|
||||
}
|
||||
delegate?.updateCellWallpaperBlur()
|
||||
if shouldUpdateSelectionHighlight {
|
||||
delegate?.updateSelectionHighlight()
|
||||
}
|
||||
@ -108,12 +99,11 @@ public class CVBackgroundContainer: ManualLayoutViewWithLayer {
|
||||
|
||||
fileprivate extension CVBackgroundContainer {
|
||||
|
||||
func updateWallpaperBlurMask() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
let isAnimating = shouldHaveUpdateScrollingContentTimer.get() > 0
|
||||
wallpaperView?.updateBlurMask(isAnimating: isAnimating)
|
||||
}
|
||||
// func updateWallpaperBlur(_ delegate: CVWallpaperBlurDelegate) {
|
||||
// AssertIsOnMainThread()
|
||||
//
|
||||
// updateCellWallpaperBlur()
|
||||
// }
|
||||
|
||||
func updateScrollingContentForAnimation(duration: TimeInterval) {
|
||||
AssertIsOnMainThread()
|
||||
@ -121,7 +111,7 @@ fileprivate extension CVBackgroundContainer {
|
||||
// To ensure a "smooth landing" of these animations, we need to continue
|
||||
// to update the mask for a short period after the animations lands.
|
||||
let duration = duration * 2
|
||||
|
||||
|
||||
startUpdateScrollingContentTimer()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + duration) { [weak self] in
|
||||
self?.stopUpdateScrollingContentTimer()
|
||||
@ -164,37 +154,60 @@ fileprivate extension CVBackgroundContainer {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension CVBackgroundContainer: CVWallpaperBlurDelegate {
|
||||
public var wallpaperBlurState: WallpaperBlurState? {
|
||||
wallpaperView?.blurProvider?.getState()
|
||||
}
|
||||
|
||||
public var isAnimating: Bool {
|
||||
shouldHaveUpdateScrollingContentTimer.get() > 0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension ConversationViewController: CVBackgroundContainerDelegate {
|
||||
var selectionHighlightView: SelectionHighlightView {
|
||||
viewState.backgroundContainer.selectionHighlightView
|
||||
backgroundContainer.selectionHighlightView
|
||||
}
|
||||
|
||||
@objc
|
||||
public func updateScrollingContent() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
viewState.backgroundContainer.updateWallpaperBlurMask()
|
||||
updateCellWallpaperBlur()
|
||||
updateSelectionHighlight()
|
||||
}
|
||||
|
||||
public func updateCellWallpaperBlur() {
|
||||
let provider = backgroundContainer.wallpaperView?.blurProvider
|
||||
for cell in collectionView.visibleCells {
|
||||
guard let cell = cell as? CVCell else {
|
||||
owsFailDebug("Invalid cell.")
|
||||
continue
|
||||
}
|
||||
cell.updateWallpaperBlur(delegate: backgroundContainer)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func updateScrollingContentForAnimation(duration: TimeInterval) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
viewState.backgroundContainer.updateScrollingContentForAnimation(duration: duration)
|
||||
backgroundContainer.updateScrollingContentForAnimation(duration: duration)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func startUpdateScrollingContentTimer() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
viewState.backgroundContainer.startUpdateScrollingContentTimer()
|
||||
backgroundContainer.startUpdateScrollingContentTimer()
|
||||
}
|
||||
|
||||
@objc
|
||||
public func stopUpdateScrollingContentTimer() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
viewState.backgroundContainer.stopUpdateScrollingContentTimer()
|
||||
backgroundContainer.stopUpdateScrollingContentTimer()
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +273,9 @@ public extension ConversationViewController {
|
||||
get { viewState.messageActionsOriginalFocusY }
|
||||
set { viewState.messageActionsOriginalFocusY = newValue }
|
||||
}
|
||||
var backgroundContainer: CVBackgroundContainer {
|
||||
get { viewState.backgroundContainer }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
|
||||
@ -33,30 +33,12 @@ extension ConversationViewController {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let wallpaperView = databaseStorage.read(block: { transaction in
|
||||
Wallpaper.view(for: self.thread,
|
||||
maskDataSource: self,
|
||||
transaction: transaction)
|
||||
Wallpaper.view(for: self.thread, transaction: transaction)
|
||||
}) else {
|
||||
viewState.backgroundContainer.set(wallpaperView: nil)
|
||||
backgroundContainer.set(wallpaperView: nil)
|
||||
return
|
||||
}
|
||||
|
||||
viewState.backgroundContainer.set(wallpaperView: wallpaperView)
|
||||
backgroundContainer.set(wallpaperView: wallpaperView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension ConversationViewController: WallpaperMaskDataSource {
|
||||
public func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) {
|
||||
for cell in collectionView.visibleCells {
|
||||
guard let cell = cell as? CVCell else {
|
||||
owsFailDebug("Invalid cell.")
|
||||
continue
|
||||
}
|
||||
cell.buildWallpaperMask(wallpaperMaskBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
public var isWallpaperPreview: Bool { false }
|
||||
}
|
||||
|
||||
@ -478,7 +478,7 @@ typedef enum : NSUInteger {
|
||||
// The view controller will only automatically adjust content insets for a
|
||||
// scrollView at index 0, so we need the collection view to remain subview index 0.
|
||||
// But the background views should appear visually behind the collection view.
|
||||
CVBackgroundContainer *backgroundContainer = self.viewState.backgroundContainer;
|
||||
CVBackgroundContainer *backgroundContainer = self.backgroundContainer;
|
||||
backgroundContainer.delegate = self;
|
||||
[self.view addSubview:backgroundContainer];
|
||||
[backgroundContainer autoPinEdgesToSuperviewEdges];
|
||||
|
||||
@ -126,14 +126,6 @@ public class MessageActionsViewController: UIViewController {
|
||||
snapshotFocusedView?.removeFromSuperview()
|
||||
snapshotFocusedView = nil
|
||||
|
||||
if let cell = focusedView as? CVCell {
|
||||
cell.willSnapshotForMessageActions()
|
||||
}
|
||||
defer {
|
||||
if let cell = focusedView as? CVCell {
|
||||
cell.didSnapshotForMessageActions()
|
||||
}
|
||||
}
|
||||
guard let snapshotView = focusedView.snapshotView(afterScreenUpdates: true) else {
|
||||
return owsFailDebug("snapshotView was unexpectedly nil")
|
||||
}
|
||||
|
||||
@ -147,7 +147,6 @@ class PreviewWallpaperViewController: UIViewController {
|
||||
case .photo(let selectedPhoto):
|
||||
owsAssertDebug(self.standalonePage == nil)
|
||||
let standalonePage = WallpaperPage(wallpaper: .photo,
|
||||
maskDataSource: mockConversationView,
|
||||
thread: thread,
|
||||
photo: selectedPhoto)
|
||||
self.standalonePage = standalonePage
|
||||
@ -165,7 +164,6 @@ class PreviewWallpaperViewController: UIViewController {
|
||||
}
|
||||
|
||||
currentPage = WallpaperPage(wallpaper: selectedWallpaper,
|
||||
maskDataSource: mockConversationView,
|
||||
thread: thread)
|
||||
blurButton.isHidden = true
|
||||
}
|
||||
@ -218,14 +216,12 @@ extension PreviewWallpaperViewController: UIPageViewControllerDataSource, UIPage
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||
guard let currentPage = currentPage, currentPage.wallpaper != .photo else { return nil }
|
||||
return WallpaperPage(wallpaper: wallpaper(before: currentPage.wallpaper),
|
||||
maskDataSource: mockConversationView,
|
||||
thread: thread)
|
||||
}
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
||||
guard let currentPage = currentPage, currentPage.wallpaper != .photo else { return nil }
|
||||
return WallpaperPage(wallpaper: wallpaper(after: currentPage.wallpaper),
|
||||
maskDataSource: mockConversationView,
|
||||
thread: thread)
|
||||
}
|
||||
|
||||
@ -290,14 +286,10 @@ private class WallpaperPage: UIViewController {
|
||||
let photo: UIImage?
|
||||
var shouldBlur = false { didSet { updatePhoto() } }
|
||||
|
||||
private weak var maskDataSource: WallpaperMaskDataSource?
|
||||
|
||||
init(wallpaper: Wallpaper,
|
||||
maskDataSource: WallpaperMaskDataSource,
|
||||
thread: TSThread?,
|
||||
photo: UIImage? = nil) {
|
||||
self.wallpaper = wallpaper
|
||||
self.maskDataSource = maskDataSource
|
||||
self.thread = thread
|
||||
self.photo = photo
|
||||
|
||||
@ -315,36 +307,27 @@ private class WallpaperPage: UIViewController {
|
||||
var wallpaperViewHeightAndWidthPriorityConstraints = [NSLayoutConstraint]()
|
||||
|
||||
var wallpaperView: WallpaperView?
|
||||
var wallpaperContentPreviewView: UIView?
|
||||
var wallpaperBlurPreviewView: UIView?
|
||||
var wallpaperPreviewView: UIView?
|
||||
|
||||
override func loadView() {
|
||||
let rootView = ManualLayoutViewWithLayer(name: "rootView")
|
||||
rootView.shouldDeactivateConstraints = false
|
||||
rootView.translatesAutoresizingMaskIntoConstraints = true
|
||||
rootView.backgroundColor = Theme.darkThemeBackgroundColor
|
||||
rootView.addLayoutBlock { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.wallpaperView?.updateBlurContentAndMask()
|
||||
}
|
||||
view = rootView
|
||||
|
||||
let shouldDim = databaseStorage.read { transaction in
|
||||
Wallpaper.shouldDim(thread: thread, transaction: transaction)
|
||||
}
|
||||
guard let wallpaperView = Wallpaper.view(for: wallpaper,
|
||||
maskDataSource: maskDataSource,
|
||||
photo: photo,
|
||||
shouldDim: shouldDim) else {
|
||||
return owsFailDebug("Failed to create photo wallpaper view")
|
||||
}
|
||||
self.wallpaperView = wallpaperView
|
||||
|
||||
let wallpaperContentPreviewView = wallpaperView.asPreviewView(mode: .contentAndDimming)
|
||||
self.wallpaperContentPreviewView = wallpaperContentPreviewView
|
||||
|
||||
let wallpaperBlurPreviewView = wallpaperView.asPreviewView(mode: .blur)
|
||||
self.wallpaperBlurPreviewView = wallpaperBlurPreviewView
|
||||
let wallpaperPreviewView = wallpaperView.asPreviewView()
|
||||
self.wallpaperPreviewView = wallpaperPreviewView
|
||||
|
||||
// If this is a photo, embed it in a scrollView for pinch & zoom
|
||||
if case .photo = wallpaper, let photo = photo {
|
||||
@ -355,17 +338,17 @@ private class WallpaperPage: UIViewController {
|
||||
scrollView.delegate = self
|
||||
view.addSubview(scrollView)
|
||||
scrollView.autoPinEdgesToSuperviewEdges()
|
||||
scrollView.addSubview(wallpaperContentPreviewView)
|
||||
scrollView.addSubview(wallpaperPreviewView)
|
||||
|
||||
wallpaperContentPreviewView.autoPinEdgesToSuperviewEdges()
|
||||
wallpaperPreviewView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
wallpaperViewWidthPriorityConstraints = [
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.width,
|
||||
to: .width,
|
||||
of: scrollView
|
||||
),
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.height,
|
||||
to: .width,
|
||||
of: scrollView,
|
||||
@ -375,12 +358,12 @@ private class WallpaperPage: UIViewController {
|
||||
wallpaperViewWidthPriorityConstraints.forEach { $0.isActive = false }
|
||||
|
||||
wallpaperViewHeightPriorityConstraints = [
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.height,
|
||||
to: .height,
|
||||
of: scrollView
|
||||
),
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.width,
|
||||
to: .height,
|
||||
of: scrollView,
|
||||
@ -390,12 +373,12 @@ private class WallpaperPage: UIViewController {
|
||||
wallpaperViewHeightPriorityConstraints.forEach { $0.isActive = false }
|
||||
|
||||
wallpaperViewHeightAndWidthPriorityConstraints = [
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.height,
|
||||
to: .height,
|
||||
of: scrollView
|
||||
),
|
||||
wallpaperContentPreviewView.autoMatch(
|
||||
wallpaperPreviewView.autoMatch(
|
||||
.width,
|
||||
to: .width,
|
||||
of: scrollView
|
||||
@ -405,24 +388,9 @@ private class WallpaperPage: UIViewController {
|
||||
|
||||
updateWallpaperConstraints(reference: view.bounds.size)
|
||||
} else {
|
||||
view.addSubview(wallpaperContentPreviewView)
|
||||
wallpaperContentPreviewView.autoPinEdgesToSuperviewEdges()
|
||||
view.addSubview(wallpaperPreviewView)
|
||||
wallpaperPreviewView.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
|
||||
view.addSubview(wallpaperBlurPreviewView)
|
||||
wallpaperBlurPreviewView.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.wallpaperView?.updateBlurContentAndMask()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
self.wallpaperView?.updateBlurContentAndMask()
|
||||
}
|
||||
|
||||
private func updatePhoto() {
|
||||
@ -491,7 +459,7 @@ private class WallpaperPage: UIViewController {
|
||||
|
||||
extension WallpaperPage: UIScrollViewDelegate {
|
||||
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||
return wallpaperContentPreviewView
|
||||
return wallpaperPreviewView
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,8 +242,7 @@ class WallpaperCell: UICollectionViewCell {
|
||||
Wallpaper.shouldDim(thread: thread, transaction: transaction)
|
||||
}
|
||||
wallpaperView = Wallpaper.view(for: wallpaper,
|
||||
maskDataSource: nil,
|
||||
shouldDim: shouldDim)?.asPreviewView(mode: .all)
|
||||
shouldDim: shouldDim)?.asPreviewView()
|
||||
|
||||
guard let wallpaperView = wallpaperView else {
|
||||
return owsFailDebug("Missing wallpaper view")
|
||||
|
||||
@ -202,12 +202,10 @@ class MiniPreviewView: UIView {
|
||||
backgroundColor = Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray05
|
||||
|
||||
let stackViewContainer: UIView
|
||||
if let wallpaperView = databaseStorage.read(
|
||||
block: { Wallpaper.view(for: thread,
|
||||
maskDataSource: nil,
|
||||
transaction: $0) }
|
||||
) {
|
||||
stackViewContainer = wallpaperView.asPreviewView(mode: .all)
|
||||
if let wallpaperView = (databaseStorage.read { transaction in
|
||||
Wallpaper.view(for: thread, transaction: transaction)
|
||||
}) {
|
||||
stackViewContainer = wallpaperView.asPreviewView()
|
||||
} else {
|
||||
stackViewContainer = UIView()
|
||||
stackViewContainer.backgroundColor = Theme.backgroundColor
|
||||
|
||||
@ -197,15 +197,15 @@ class MockConversationView: UIView {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension MockConversationView: WallpaperMaskDataSource {
|
||||
func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder) {
|
||||
outgoingMessageView.buildWallpaperMask(wallpaperMaskBuilder)
|
||||
incomingMessageView.buildWallpaperMask(wallpaperMaskBuilder)
|
||||
dateHeaderView.buildWallpaperMask(wallpaperMaskBuilder)
|
||||
}
|
||||
|
||||
var isWallpaperPreview: Bool { true }
|
||||
}
|
||||
// extension MockConversationView: WallpaperMaskDataSource {
|
||||
// func updateWallpaperBlur(_ delegate: CVWallpaperBlurDelegate) {
|
||||
// outgoingMessageView.buildWallpaperMask(delegate)
|
||||
// incomingMessageView.buildWallpaperMask(delegate)
|
||||
// dateHeaderView.buildWallpaperMask(delegate)
|
||||
// }
|
||||
//
|
||||
// var isWallpaperPreview: Bool { true }
|
||||
// }
|
||||
|
||||
// MARK: - Mock Classes
|
||||
|
||||
|
||||
@ -142,7 +142,6 @@ public enum Wallpaper: String, CaseIterable {
|
||||
}
|
||||
|
||||
public static func view(for thread: TSThread? = nil,
|
||||
maskDataSource: WallpaperMaskDataSource?,
|
||||
transaction: SDSAnyReadTransaction) -> WallpaperView? {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
@ -176,7 +175,6 @@ public enum Wallpaper: String, CaseIterable {
|
||||
dimInDarkMode(for: thread, transaction: transaction))
|
||||
|
||||
guard let view = view(for: wallpaper,
|
||||
maskDataSource: maskDataSource,
|
||||
photo: photo,
|
||||
shouldDim: shouldDim) else {
|
||||
return nil
|
||||
@ -192,7 +190,6 @@ public enum Wallpaper: String, CaseIterable {
|
||||
}
|
||||
|
||||
public static func view(for wallpaper: Wallpaper,
|
||||
maskDataSource: WallpaperMaskDataSource?,
|
||||
photo: UIImage? = nil,
|
||||
shouldDim: Bool) -> WallpaperView? {
|
||||
AssertIsOnMainThread()
|
||||
@ -215,9 +212,7 @@ public enum Wallpaper: String, CaseIterable {
|
||||
}() else {
|
||||
return nil
|
||||
}
|
||||
return WallpaperView(mode: mode,
|
||||
maskDataSource: maskDataSource,
|
||||
shouldDim: shouldDim)
|
||||
return WallpaperView(mode: mode, shouldDim: shouldDim)
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,49 +351,6 @@ fileprivate extension Wallpaper {
|
||||
|
||||
// MARK: -
|
||||
|
||||
public struct WallpaperMaskBuilder {
|
||||
|
||||
fileprivate let maskPath = UIBezierPath()
|
||||
public let referenceView: UIView
|
||||
fileprivate let isAnimating: Bool
|
||||
|
||||
public func append(blurPath: UIBezierPath) {
|
||||
maskPath.append(blurPath)
|
||||
}
|
||||
|
||||
public func append(blurView: UIView?) {
|
||||
guard let blurView = blurView else {
|
||||
Logger.warn("Missing blurView.")
|
||||
return
|
||||
}
|
||||
|
||||
let blurFrame: CGRect
|
||||
if isAnimating,
|
||||
let srcLayer = blurView.layer.presentation(),
|
||||
let dstLayer = referenceView.layer.presentation() {
|
||||
blurFrame = dstLayer.convert(srcLayer.bounds, from: srcLayer)
|
||||
} else {
|
||||
blurFrame = referenceView.convert(blurView.bounds, from: blurView)
|
||||
}
|
||||
|
||||
let blurPath: UIBezierPath = {
|
||||
return UIBezierPath(roundedRect: blurFrame,
|
||||
byRoundingCorners: blurView.layer.maskedCorners.asUIRectCorner,
|
||||
cornerRadii: .square(blurView.layer.cornerRadius))
|
||||
}()
|
||||
append(blurPath: blurPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public protocol WallpaperMaskDataSource: class {
|
||||
func buildWallpaperMask(_ wallpaperMaskBuilder: WallpaperMaskBuilder)
|
||||
var isWallpaperPreview: Bool { get }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class WallpaperView {
|
||||
fileprivate enum Mode {
|
||||
case solidColor(solidColor: UIColor)
|
||||
@ -406,22 +358,18 @@ public class WallpaperView {
|
||||
case image(image: UIImage)
|
||||
}
|
||||
|
||||
private var _blurView: BlurView?
|
||||
|
||||
public var blurView: UIView? { _blurView }
|
||||
|
||||
public private(set) var contentView: UIView?
|
||||
|
||||
public private(set) var dimmingView: UIView?
|
||||
|
||||
public private(set) var blurProvider: WallpaperBlurProvider?
|
||||
|
||||
private let mode: Mode
|
||||
|
||||
fileprivate init(mode: Mode,
|
||||
maskDataSource: WallpaperMaskDataSource?,
|
||||
shouldDim: Bool) {
|
||||
fileprivate init(mode: Mode, shouldDim: Bool) {
|
||||
self.mode = mode
|
||||
|
||||
configure(maskDataSource: maskDataSource, shouldDim: shouldDim)
|
||||
configure(shouldDim: shouldDim)
|
||||
}
|
||||
|
||||
@available(swift, obsoleted: 1.0)
|
||||
@ -429,213 +377,167 @@ public class WallpaperView {
|
||||
owsFail("Do not use this initializer.")
|
||||
}
|
||||
|
||||
public enum PreviewMode {
|
||||
case all
|
||||
case blur
|
||||
case contentAndDimming
|
||||
|
||||
fileprivate var showContentAndDimming: Bool {
|
||||
switch self {
|
||||
case .all, .contentAndDimming:
|
||||
return true
|
||||
case .blur:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var showBlur: Bool {
|
||||
switch self {
|
||||
case .all, .contentAndDimming:
|
||||
return false
|
||||
case .blur:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func asPreviewView(mode: PreviewMode) -> UIView {
|
||||
public func asPreviewView() -> UIView {
|
||||
let previewView = UIView.container()
|
||||
if mode.showContentAndDimming,
|
||||
let contentView = self.contentView {
|
||||
if let contentView = self.contentView {
|
||||
previewView.addSubview(contentView)
|
||||
contentView.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
if mode.showContentAndDimming,
|
||||
let dimmingView = self.dimmingView {
|
||||
if let dimmingView = self.dimmingView {
|
||||
previewView.addSubview(dimmingView)
|
||||
dimmingView.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
if mode.showBlur,
|
||||
let blurView = self.blurView {
|
||||
previewView.addSubview(blurView)
|
||||
blurView.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
return previewView
|
||||
}
|
||||
|
||||
private func configure(maskDataSource: WallpaperMaskDataSource?,
|
||||
shouldDim: Bool) {
|
||||
private func configure(shouldDim: Bool) {
|
||||
func addDimmingViewIfNecessary() {
|
||||
if shouldDim {
|
||||
let dimmingView = UIView()
|
||||
dimmingView.backgroundColor = .ows_blackAlpha20
|
||||
self.dimmingView = dimmingView
|
||||
}
|
||||
}
|
||||
|
||||
let contentView: UIView = {
|
||||
switch mode {
|
||||
case .solidColor(let solidColor):
|
||||
let isWallpaperPreview = maskDataSource?.isWallpaperPreview ?? false
|
||||
if isWallpaperPreview {
|
||||
let contentView = OWSLayerView(frame: .zero) { [weak self] _ in
|
||||
self?.updateBlurContentAndMask()
|
||||
}
|
||||
contentView.backgroundColor = solidColor
|
||||
return contentView
|
||||
} else {
|
||||
let contentView = UIView()
|
||||
contentView.backgroundColor = solidColor
|
||||
return contentView
|
||||
}
|
||||
// Bake the dimming into the color.
|
||||
let color = (shouldDim
|
||||
? solidColor.blended(with: .ows_black, alpha: 0.2)
|
||||
: solidColor)
|
||||
|
||||
let contentView = UIView()
|
||||
contentView.backgroundColor = color
|
||||
return contentView
|
||||
case .gradientView(let gradientView):
|
||||
|
||||
addDimmingViewIfNecessary()
|
||||
|
||||
return gradientView
|
||||
case .image(let image):
|
||||
let imageView = UIImageView(image: image)
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.clipsToBounds = true
|
||||
|
||||
// TODO: Bake dimming into the image.
|
||||
addDimmingViewIfNecessary()
|
||||
|
||||
return imageView
|
||||
}
|
||||
}()
|
||||
self.contentView = contentView
|
||||
|
||||
addBlurView(contentView: contentView, maskDataSource: maskDataSource)
|
||||
|
||||
if shouldDim {
|
||||
let dimmingView = UIView()
|
||||
dimmingView.backgroundColor = .ows_blackAlpha20
|
||||
self.dimmingView = dimmingView
|
||||
}
|
||||
addBlurProvider(contentView: contentView)
|
||||
}
|
||||
|
||||
private func addBlurView(contentView: UIView,
|
||||
maskDataSource: WallpaperMaskDataSource?) {
|
||||
guard let maskDataSource = maskDataSource else {
|
||||
return
|
||||
}
|
||||
let blurView = BlurView(contentView: contentView, maskDataSource: maskDataSource)
|
||||
_blurView = blurView
|
||||
private func addBlurProvider(contentView: UIView) {
|
||||
self.blurProvider = WallpaperBlurProvider(contentView: contentView)
|
||||
}
|
||||
|
||||
public func updateBlurContentAndMask(isAnimating: Bool = false) {
|
||||
_blurView?.updateContentAndMask(isAnimating: isAnimating)
|
||||
// public func updateBlurContentAndMask(isAnimating: Bool = false) {
|
||||
// _blurView?.updateContentAndMask(isAnimating: isAnimating)
|
||||
// }
|
||||
//
|
||||
// public func updateBlurMask(isAnimating: Bool = false) {
|
||||
// _blurView?.updateMask(isAnimating: isAnimating)
|
||||
// }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private struct WallpaperBlurToken: Equatable {
|
||||
let contentSize: CGSize
|
||||
let isDarkThemeEnabled: Bool
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class WallpaperBlurState {
|
||||
public let image: UIImage
|
||||
public let referenceView: UIView
|
||||
fileprivate let token: WallpaperBlurToken
|
||||
|
||||
private static let idCounter = AtomicUInt(0)
|
||||
public let id: UInt = WallpaperBlurState.idCounter.increment()
|
||||
|
||||
fileprivate init(image: UIImage,
|
||||
referenceView: UIView,
|
||||
token: WallpaperBlurToken) {
|
||||
self.image = image
|
||||
self.referenceView = referenceView
|
||||
self.token = token
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class WallpaperBlurProvider {
|
||||
private let contentView: UIView
|
||||
|
||||
private var state: WallpaperBlurState?
|
||||
|
||||
init(contentView: UIView) {
|
||||
self.contentView = contentView
|
||||
}
|
||||
|
||||
public func updateBlurMask(isAnimating: Bool = false) {
|
||||
_blurView?.updateMask(isAnimating: isAnimating)
|
||||
@available(swift, obsoleted: 1.0)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
public func reset() {
|
||||
self.state = nil
|
||||
}
|
||||
|
||||
private class BlurView: UIImageView {
|
||||
private let contentView: UIView
|
||||
private let maskLayer = CAShapeLayer()
|
||||
private weak var maskDataSource: WallpaperMaskDataSource?
|
||||
public func getState() -> WallpaperBlurState? {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
init(contentView: UIView, maskDataSource: WallpaperMaskDataSource) {
|
||||
self.contentView = contentView
|
||||
self.maskDataSource = maskDataSource
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.contentMode = .scaleAspectFill
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.layer.mask = maskLayer
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(themeDidChange),
|
||||
name: .ThemeDidChange,
|
||||
object: nil)
|
||||
// De-bounce.
|
||||
let bounds = contentView.bounds
|
||||
let isDarkThemeEnabled = Theme.isDarkThemeEnabled
|
||||
let newToken = WallpaperBlurToken(contentSize: bounds.size,
|
||||
isDarkThemeEnabled: isDarkThemeEnabled)
|
||||
if let state = self.state,
|
||||
state.token == newToken {
|
||||
return state
|
||||
}
|
||||
|
||||
@available(swift, obsoleted: 1.0)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
reset()
|
||||
|
||||
@objc
|
||||
private func themeDidChange() {
|
||||
updateContent()
|
||||
}
|
||||
|
||||
func updateContentAndMask(isAnimating: Bool) {
|
||||
updateContent()
|
||||
updateMask(isAnimating: isAnimating)
|
||||
}
|
||||
|
||||
func updateMask(isAnimating: Bool) {
|
||||
guard let maskDataSource = self.maskDataSource else {
|
||||
owsFailDebug("Missing maskDataSource.")
|
||||
resetMask()
|
||||
return
|
||||
do {
|
||||
guard bounds.width > 0, bounds.height > 0 else {
|
||||
return nil
|
||||
}
|
||||
let builder = WallpaperMaskBuilder(referenceView: self, isAnimating: isAnimating)
|
||||
maskDataSource.buildWallpaperMask(builder)
|
||||
maskLayer.path = builder.maskPath.cgPath
|
||||
}
|
||||
|
||||
private struct ContentToken: Equatable {
|
||||
let contentSize: CGSize
|
||||
let isDarkThemeEnabled: Bool
|
||||
}
|
||||
private var contentToken: ContentToken?
|
||||
|
||||
func updateContent() {
|
||||
// De-bounce.
|
||||
let isDarkThemeEnabled = Theme.isDarkThemeEnabled
|
||||
let newContentToken = ContentToken(contentSize: bounds.size,
|
||||
isDarkThemeEnabled: isDarkThemeEnabled)
|
||||
guard contentToken != newContentToken else {
|
||||
return
|
||||
guard let contentImage = contentView.renderAsImage() else {
|
||||
owsFailDebug("Could not render contentView.")
|
||||
return nil
|
||||
}
|
||||
|
||||
do {
|
||||
guard bounds.width > 0, bounds.height > 0 else {
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
guard let contentImage = contentView.renderAsImage() else {
|
||||
owsFailDebug("Could not render contentView.")
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
// We approximate the behavior of UIVisualEffectView(effect: UIBlurEffect(style: .regular)).
|
||||
let tintColor: UIColor = (isDarkThemeEnabled
|
||||
? UIColor.ows_black.withAlphaComponent(0.9)
|
||||
: .ows_whiteAlpha60)
|
||||
let resizeFactor: CGFloat = 8
|
||||
let resizeDimension = contentImage.size.largerAxis / resizeFactor
|
||||
guard let scaledImage = contentImage.resized(withMaxDimensionPoints: resizeDimension) else {
|
||||
owsFailDebug("Could not resize contentImage.")
|
||||
resetContent()
|
||||
return
|
||||
}
|
||||
let blurRadius: CGFloat = 32 / resizeFactor
|
||||
let blurredImage = try scaledImage.withGausianBlur(radius: blurRadius,
|
||||
tintColor: tintColor)
|
||||
self.image = blurredImage
|
||||
self.contentToken = newContentToken
|
||||
} catch {
|
||||
owsFailDebug("Error: \(error).")
|
||||
resetContent()
|
||||
// We approximate the behavior of UIVisualEffectView(effect: UIBlurEffect(style: .regular)).
|
||||
// let tintColor: UIColor = (isDarkThemeEnabled
|
||||
// ? UIColor.ows_black.withAlphaComponent(0.9)
|
||||
// : UIColor.white.withAlphaComponent(0.6)
|
||||
let tintColor: UIColor = (isDarkThemeEnabled
|
||||
? UIColor.ows_black.withAlphaComponent(0.3)
|
||||
: UIColor.white.withAlphaComponent(0.3))
|
||||
let resizeFactor: CGFloat = 8
|
||||
let resizeDimension = contentImage.size.largerAxis / resizeFactor
|
||||
guard let scaledImage = contentImage.resized(withMaxDimensionPoints: resizeDimension) else {
|
||||
owsFailDebug("Could not resize contentImage.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
resetContent()
|
||||
resetMask()
|
||||
}
|
||||
|
||||
private func resetMask() {
|
||||
maskLayer.path = nil
|
||||
}
|
||||
|
||||
private func resetContent() {
|
||||
image = nil
|
||||
contentToken = nil
|
||||
let blurRadius: CGFloat = 1 / resizeFactor
|
||||
// let blurRadius: CGFloat = 32 / resizeFactor
|
||||
let blurredImage = try scaledImage.withGausianBlur(radius: blurRadius,
|
||||
tintColor: tintColor)
|
||||
let state = WallpaperBlurState(image: blurredImage,
|
||||
referenceView: contentView,
|
||||
token: newToken)
|
||||
self.state = state
|
||||
return state
|
||||
} catch {
|
||||
owsFailDebug("Error: \(error).")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ public class DebugFlags: BaseFlags {
|
||||
public static let suppressBackgroundActivity = false
|
||||
|
||||
@objc
|
||||
public static let reduceLogChatter = false && build.includes(.dev)
|
||||
public static let reduceLogChatter = true && build.includes(.dev)
|
||||
|
||||
@objc
|
||||
public static let logSQLQueries = build.includes(.dev) && !reduceLogChatter
|
||||
|
||||
Loading…
Reference in New Issue
Block a user