Add blur setting to wallpaper preview

This commit is contained in:
Nora Trapp 2021-01-22 16:35:09 -08:00
parent 536e4d0b66
commit fa3c368b77
9 changed files with 479 additions and 21 deletions

View File

@ -1,23 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "check-circle-filled-16@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "check-circle-filled-16@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "check-circle-filled-16@3x.png",
"scale" : "3x"
"filename" : "check-circle-solid-16 1.pdf",
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 605 B

View File

@ -0,0 +1,155 @@
%PDF-1.7
1 0 obj
<< /BBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources << >>
/Subtype /Form
/Length 2 0 R
/Group << /Type /Group
/S /Transparency
>>
/Type /XObject
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.154968 cm
1.000000 1.000000 1.000000 scn
8.000004 16.154968 m
6.417753 16.154968 4.871035 15.685777 3.555442 14.806726 c
2.239848 13.927673 1.214468 12.678245 0.608967 11.216436 c
0.003466 9.754627 -0.154960 8.146094 0.153721 6.594246 c
0.462403 5.042397 1.224329 3.616934 2.343149 2.498114 c
3.461969 1.379293 4.887434 0.617368 6.439281 0.308686 c
7.991129 0.000004 9.599662 0.158431 11.061471 0.763932 c
12.523280 1.369433 13.772710 2.394814 14.651761 3.710406 c
15.530812 5.025999 16.000004 6.572718 16.000004 8.154968 c
16.000004 10.276700 15.157149 12.311531 13.656858 13.811823 c
12.156568 15.312114 10.121736 16.154968 8.000004 16.154968 c
8.000004 16.154968 l
h
6.760004 4.594968 m
3.760004 7.594968 l
4.820004 8.664968 l
6.760004 6.714969 l
11.180004 11.154968 l
12.240004 10.094969 l
6.760004 4.594968 l
h
f
n
Q
endstream
endobj
2 0 obj
901
endobj
3 0 obj
<< /BBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources << >>
/Subtype /Form
/Length 4 0 R
/Group << /Type /Group
/S /Transparency
>>
/Type /XObject
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.000000 0.000000 0.000000 scn
0.000000 16.000000 m
16.000000 16.000000 l
16.000000 0.000000 l
0.000000 0.000000 l
0.000000 16.000000 l
h
f
n
Q
endstream
endobj
4 0 obj
232
endobj
5 0 obj
<< /XObject << /X1 1 0 R >>
/ExtGState << /E1 << /SMask << /Type /Mask
/G 3 0 R
/S /Alpha
>>
/Type /ExtGState
>> >>
>>
endobj
6 0 obj
<< /Length 7 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
/E1 gs
/X1 Do
Q
endstream
endobj
7 0 obj
46
endobj
8 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources 5 0 R
/Contents 6 0 R
/Parent 9 0 R
>>
endobj
9 0 obj
<< /Kids [ 8 0 R ]
/Count 1
/Type /Pages
>>
endobj
10 0 obj
<< /Type /Catalog
/Pages 9 0 R
>>
endobj
xref
0 11
0000000000 65535 f
0000000010 00000 n
0000001159 00000 n
0000001181 00000 n
0000001661 00000 n
0000001683 00000 n
0000001981 00000 n
0000002083 00000 n
0000002104 00000 n
0000002277 00000 n
0000002351 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 10 0 R
/Size 11
>>
startxref
2411
%%EOF

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "circle-outline-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,160 @@
%PDF-1.7
1 0 obj
<< /BBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources << >>
/Subtype /Form
/Length 2 0 R
/Group << /Type /Group
/S /Transparency
>>
/Type /XObject
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.154968 cm
1.000000 1.000000 1.000000 scn
8.000004 14.654968 m
9.285583 14.654968 10.542291 14.273750 11.611210 13.559521 c
12.680129 12.845291 13.513250 11.830131 14.005220 10.642410 c
14.497190 9.454691 14.625912 8.147758 14.375108 6.886881 c
14.124304 5.626004 13.505239 4.467815 12.596198 3.558774 c
11.687157 2.649733 10.528968 2.030668 9.268091 1.779864 c
8.007215 1.529060 6.700281 1.657782 5.512561 2.149752 c
4.324841 2.641722 3.309680 3.474843 2.595451 4.543762 c
1.881222 5.612681 1.500004 6.869390 1.500004 8.154968 c
1.502648 9.878063 2.188317 11.529827 3.406731 12.748241 c
4.625144 13.966655 6.276909 14.652324 8.000004 14.654968 c
8.000004 14.654968 l
h
8.000004 16.154968 m
6.417753 16.154968 4.871035 15.685777 3.555442 14.806726 c
2.239848 13.927673 1.214468 12.678245 0.608967 11.216436 c
0.003466 9.754627 -0.154960 8.146094 0.153721 6.594246 c
0.462403 5.042397 1.224329 3.616934 2.343149 2.498114 c
3.461969 1.379293 4.887434 0.617368 6.439281 0.308686 c
7.991129 0.000004 9.599662 0.158431 11.061471 0.763932 c
12.523280 1.369433 13.772710 2.394814 14.651761 3.710406 c
15.530812 5.025999 16.000004 6.572718 16.000004 8.154968 c
16.000004 10.276700 15.157149 12.311531 13.656858 13.811823 c
12.156568 15.312114 10.121736 16.154968 8.000004 16.154968 c
8.000004 16.154968 l
h
f
n
Q
endstream
endobj
2 0 obj
1383
endobj
3 0 obj
<< /BBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources << >>
/Subtype /Form
/Length 4 0 R
/Group << /Type /Group
/S /Transparency
>>
/Type /XObject
>>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.000000 0.000000 0.000000 scn
0.000000 16.000000 m
16.000000 16.000000 l
16.000000 0.000000 l
0.000000 0.000000 l
0.000000 16.000000 l
h
f
n
Q
endstream
endobj
4 0 obj
232
endobj
5 0 obj
<< /XObject << /X1 1 0 R >>
/ExtGState << /E1 << /SMask << /Type /Mask
/G 3 0 R
/S /Alpha
>>
/Type /ExtGState
>> >>
>>
endobj
6 0 obj
<< /Length 7 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
/E1 gs
/X1 Do
Q
endstream
endobj
7 0 obj
46
endobj
8 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
/Resources 5 0 R
/Contents 6 0 R
/Parent 9 0 R
>>
endobj
9 0 obj
<< /Kids [ 8 0 R ]
/Count 1
/Type /Pages
>>
endobj
10 0 obj
<< /Type /Catalog
/Pages 9 0 R
>>
endobj
xref
0 11
0000000000 65535 f
0000000010 00000 n
0000001641 00000 n
0000001664 00000 n
0000002144 00000 n
0000002166 00000 n
0000002464 00000 n
0000002566 00000 n
0000002587 00000 n
0000002760 00000 n
0000002834 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 10 0 R
/Size 11
>>
startxref
2894
%%EOF

View File

@ -11,6 +11,7 @@ class PreviewWallpaperViewController: UIViewController {
}
private(set) var mode: Mode { didSet { modeDidChange() }}
let thread: TSThread?
lazy var blurButton = BlurButton { [weak self] shouldBlur in self?.standalonePage?.shouldBlur = shouldBlur }
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
@ -90,6 +91,10 @@ class PreviewWallpaperViewController: UIViewController {
safeAreaCover.autoPinEdge(toSuperviewEdge: .bottom)
safeAreaCover.autoPinWidthToSuperview()
safeAreaCover.autoPinEdge(.top, to: .bottom, of: buttonStack)
view.addSubview(blurButton)
blurButton.autoPinEdge(.bottom, to: .top, of: buttonStack, withOffset: -24)
blurButton.autoHCenterInSuperview()
}
override func viewDidLoad() {
@ -129,7 +134,7 @@ class PreviewWallpaperViewController: UIViewController {
}
}
private var standalonePage: UIViewController?
private var standalonePage: WallpaperPage?
func modeDidChange() {
switch mode {
case .photo(let selectedPhoto):
@ -139,6 +144,7 @@ class PreviewWallpaperViewController: UIViewController {
view.insertSubview(standalonePage.view, at: 0)
addChild(standalonePage)
standalonePage.view.autoPinEdgesToSuperviewEdges()
blurButton.isHidden = false
case .preset(let selectedWallpaper):
if pageViewController.view.superview == nil {
view.insertSubview(pageViewController.view, at: 0)
@ -149,6 +155,7 @@ class PreviewWallpaperViewController: UIViewController {
}
currentPage = WallpaperPage(wallpaper: selectedWallpaper)
blurButton.isHidden = true
}
mockConversationView.mode = buildMockConversationMode()
@ -260,11 +267,15 @@ extension PreviewWallpaperViewController: UIPageViewControllerDataSource, UIPage
private class WallpaperPage: UIViewController {
let wallpaper: Wallpaper
weak var photo: UIImage?
let photo: UIImage?
var shouldBlur = false { didSet { updatePhoto() } }
init(wallpaper: Wallpaper, photo: UIImage? = nil) {
self.wallpaper = wallpaper
self.photo = photo
super.init(nibName: nil, bundle: nil)
if photo != nil { prepareBlurredPhoto() }
}
required init?(coder: NSCoder) {
@ -350,13 +361,68 @@ private class WallpaperPage: UIViewController {
}
}
private func updatePhoto() {
guard let wallpaperImageView = wallpaperView as? UIImageView else { return }
UIView.transition(with: wallpaperImageView, duration: 0.2, options: .transitionCrossDissolve) {
wallpaperImageView.image = self.shouldBlur ? self.blurredPhoto : self.photo
} completion: { _ in }
}
private var blurredPhoto: UIImage?
private func prepareBlurredPhoto() {
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
guard let self = self else { return }
guard let clampFilter = CIFilter(name: "CIAffineClamp") else {
return owsFailDebug("Failed to create blur filter")
}
guard let blurFilter = CIFilter(name: "CIGaussianBlur", parameters: [kCIInputRadiusKey: 3]) else {
return owsFailDebug("Failed to create blur filter")
}
guard let photo = self.photo else {
return owsFailDebug("Could not load src image.")
}
guard let resizedPhoto = photo.resized(withMaxDimensionPixels: 300), let resizedCGPhoto = resizedPhoto.cgImage else {
return owsFailDebug("Failed to downsize image for blur")
}
// In order to get a nice edge-to-edge blur, we must apply a clamp filter and *then* the blur filter.
let inputPhoto = CIImage(cgImage: resizedCGPhoto)
clampFilter.setDefaults()
clampFilter.setValue(inputPhoto, forKey: kCIInputImageKey)
guard let clampOutput = clampFilter.outputImage else {
return owsFailDebug("Failed to clamp src image")
}
blurFilter.setValue(clampOutput, forKey: kCIInputImageKey)
guard let blurredOutput = blurFilter.value(forKey: kCIOutputImageKey) as? CIImage else {
return owsFailDebug("Failed to blur clamped image")
}
let context = CIContext(options: nil)
guard let blurredPhoto = context.createCGImage(blurredOutput, from: inputPhoto.extent) else {
return owsFailDebug("Failed to create CGImage from blurred output")
}
self.blurredPhoto = UIImage(cgImage: blurredPhoto)
// Once the blur is ready, update any content in case the user already blurred
DispatchQueue.main.async { [weak self] in
self?.updatePhoto()
}
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { _ in
self.updateWallpaperConstraints(reference: size)
} completion: { _ in
}
} completion: { _ in }
}
private var previousReferenceSize: CGSize = .zero
@ -401,3 +467,76 @@ extension WallpaperPage: UIScrollViewDelegate {
return wallpaperView
}
}
class BlurButton: UIButton {
let checkImageView = UIImageView()
let label = UILabel()
let action: (Bool) -> Void
let backgroundView: UIView = {
if UIAccessibility.isReduceTransparencyEnabled {
let backgroundView = UIView()
backgroundView.backgroundColor = .ows_blackAlpha80
return backgroundView
} else {
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
return blurView
}
}()
init(action: @escaping (Bool) -> Void) {
self.action = action
super.init(frame: .zero)
addTarget(self, action: #selector(didTap), for: .touchUpInside)
layoutMargins = UIEdgeInsets(top: 3, leading: 10, bottom: 3, trailing: 12)
autoSetDimension(.height, toSize: 28, relation: .greaterThanOrEqual)
backgroundView.clipsToBounds = true
backgroundView.isUserInteractionEnabled = false
addSubview(backgroundView)
backgroundView.autoPinEdgesToSuperviewEdges()
addSubview(checkImageView)
checkImageView.autoPinEdge(toSuperviewMargin: .leading)
checkImageView.autoPinHeightToSuperviewMargins()
checkImageView.autoSetDimension(.width, toSize: 16)
checkImageView.contentMode = .scaleAspectFit
checkImageView.isUserInteractionEnabled = false
label.font = .ows_semiboldFont(withSize: 14)
label.textColor = .white
label.text = NSLocalizedString("WALLPAPER_PREVIEW_BLUR_BUTTON",
comment: "Blur button on wallpaper preview.")
addSubview(label)
label.autoPinHeightToSuperviewMargins()
label.autoPinEdge(toSuperviewMargin: .trailing)
label.autoPinEdge(.leading, to: .trailing, of: checkImageView, withOffset: 10)
label.isUserInteractionEnabled = false
isSelected = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
backgroundView.layer.cornerRadius = height / 2
}
override var isSelected: Bool {
didSet {
UIView.transition(with: checkImageView, duration: 0.15, options: .transitionCrossDissolve) {
self.checkImageView.image = self.isSelected ? #imageLiteral(resourceName: "check-circle-filled-16") : #imageLiteral(resourceName: "circle-outline-16")
} completion: { _ in }
}
}
@objc
func didTap() {
isSelected = !isSelected
action(isSelected)
}
}

View File

@ -5182,6 +5182,9 @@
/* Placeholder text for header of the wallpaper mini preview */
"WALLPAPER_MINI_PREVIEW_CONTACT_NAME" = "Contact Name";
/* Blur button on wallpaper preview. */
"WALLPAPER_PREVIEW_BLUR_BUTTON" = "Blur Photo";
/* The incoming bubble text when setting a photo */
"WALLPAPER_PREVIEW_INCOMING_MESSAGE_PHOTO" = "Pinch to zoom, drag to adjust.";