Path: blob/main/Signal/src/ViewControllers/Wallpapers/SetWallpaperViewController.swift
1 views
//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import CoreServices
import SignalServiceKit
import SignalUI
import UniformTypeIdentifiers
class SetWallpaperViewController: OWSTableViewController2 {
private lazy var collectionView = WallpaperCollectionView(container: self, shouldDimInDarkMode: shouldDimInDarkMode) { [weak self] wallpaper in
guard let self else { return }
let vc = PreviewWallpaperViewController(
mode: .preset(selectedWallpaper: wallpaper),
thread: self.thread,
delegate: self,
)
self.presentFullScreen(UINavigationController(rootViewController: vc), animated: true)
}
static func load(thread: TSThread?, tx: DBReadTransaction) -> SetWallpaperViewController {
return SetWallpaperViewController(
thread: thread,
shouldDimInDarkMode: DependenciesBridge.shared.wallpaperStore.fetchDimInDarkModeForRendering(
for: thread?.uniqueId,
tx: tx,
),
)
}
private let thread: TSThread?
private let shouldDimInDarkMode: Bool
init(thread: TSThread?, shouldDimInDarkMode: Bool) {
self.thread = thread
self.shouldDimInDarkMode = shouldDimInDarkMode
super.init()
}
override func viewDidLoad() {
super.viewDidLoad()
title = OWSLocalizedString("SET_WALLPAPER_TITLE", comment: "Title for the set wallpaper settings view.")
updateTableContents()
}
private var previousReferenceSize: CGSize = .zero
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let referenceSize = view.bounds.size - CGSize(width: view.safeAreaInsets.totalWidth, height: 0)
guard referenceSize != previousReferenceSize else { return }
previousReferenceSize = referenceSize
updateCollectionViewSize(reference: referenceSize)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { _ in
self.updateCollectionViewSize(reference: size - CGSize(width: self.view.safeAreaInsets.totalWidth, height: 0))
} completion: { _ in
}
}
func updateCollectionViewSize(reference: CGSize) {
collectionView.updateLayout(reference: reference)
tableView.reloadRows(at: [IndexPath(row: 0, section: 1)], with: .none)
}
@objc
private func updateTableContents() {
let contents = OWSTableContents()
let photosSection = OWSTableSection()
photosSection.customHeaderHeight = 14
let choosePhotoItem = OWSTableItem.disclosureItem(
icon: .buttonPhotoLibrary,
withText: OWSLocalizedString(
"SET_WALLPAPER_CHOOSE_PHOTO",
comment: "Title for the wallpaper choose from photos option",
),
) { [weak self] in
guard let self else { return }
let vc = UIImagePickerController()
vc.delegate = self
vc.sourceType = .photoLibrary
vc.mediaTypes = [UTType.image.identifier]
self.presentFormSheet(vc, animated: true)
}
photosSection.add(choosePhotoItem)
contents.add(photosSection)
let presetsSection = OWSTableSection()
presetsSection.headerTitle = OWSLocalizedString(
"SET_WALLPAPER_PRESETS",
comment: "Title for the wallpaper presets section",
)
let presetsItem = OWSTableItem { [weak self] in
let cell = OWSTableItem.newCell()
guard let self else { return cell }
cell.contentView.addSubview(self.collectionView)
self.collectionView.autoPinEdgesToSuperviewMargins()
return cell
} actionBlock: {}
presetsSection.add(presetsItem)
contents.add(presetsSection)
self.contents = contents
}
}
extension SetWallpaperViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any],
) {
guard let rawImage = info[.originalImage] as? UIImage else {
return owsFailDebug("Missing image")
}
let vc = PreviewWallpaperViewController(
mode: .photo(selectedPhoto: rawImage),
thread: thread,
delegate: self,
)
picker.dismiss(animated: true) {
self.presentFullScreen(UINavigationController(rootViewController: vc), animated: true)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
extension SetWallpaperViewController: PreviewWallpaperDelegate {
func previewWallpaperDidCancel(_ vc: PreviewWallpaperViewController) {
vc.dismiss(animated: true)
}
func previewWallpaperDidComplete(_ vc: PreviewWallpaperViewController) {
navigationController?.popViewController(animated: false)
vc.dismiss(animated: true)
}
}
private class WallpaperCollectionView: UICollectionView {
private let shouldDimInDarkMode: Bool
private let flowLayout = UICollectionViewFlowLayout()
private let selectionHandler: (Wallpaper) -> Void
private lazy var heightConstraint = autoSetDimension(.height, toSize: 0)
private weak var container: OWSTableViewController2!
init(
container: OWSTableViewController2,
shouldDimInDarkMode: Bool,
selectionHandler: @escaping (Wallpaper) -> Void,
) {
self.container = container
self.shouldDimInDarkMode = shouldDimInDarkMode
self.selectionHandler = selectionHandler
flowLayout.minimumLineSpacing = 4
flowLayout.minimumInteritemSpacing = 2
super.init(frame: .zero, collectionViewLayout: flowLayout)
delegate = self
dataSource = self
contentInset = UIEdgeInsets(hMargin: 0, vMargin: 8)
isScrollEnabled = false
backgroundColor = .clear
register(WallpaperCell.self, forCellWithReuseIdentifier: WallpaperCell.reuseIdentifier)
}
func updateLayout(reference: CGSize) {
AssertIsOnMainThread()
let numberOfColumns: CGFloat = 3
let numberOfRows = CGFloat(Wallpaper.defaultWallpapers.count) / numberOfColumns
let availableWidth = reference.width -
((OWSTableViewController2.cellHInnerMargin * 2) + container.cellOuterInsets.totalWidth + 8 + safeAreaInsets.totalWidth)
let itemWidth = availableWidth / numberOfColumns
let itemHeight = itemWidth / CurrentAppContext().frame.size.aspectRatio
flowLayout.itemSize = CGSize(width: itemWidth, height: itemHeight)
flowLayout.invalidateLayout()
heightConstraint.constant = numberOfRows * itemHeight + ((numberOfRows - 1) * flowLayout.minimumLineSpacing) + contentInset.totalHeight
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension WallpaperCollectionView: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
Wallpaper.defaultWallpapers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: WallpaperCell.reuseIdentifier,
for: indexPath,
)
guard let wallpaperCell = cell as? WallpaperCell else {
owsFailDebug("Dequeued unexpected cell")
return cell
}
guard let wallpaper = Wallpaper.defaultWallpapers[safe: indexPath.row] else {
owsFailDebug("Missing wallpaper for index \(indexPath.row)")
return cell
}
wallpaperCell.configure(for: wallpaper, shouldDimInDarkMode: shouldDimInDarkMode)
return wallpaperCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let wallpaper = Wallpaper.defaultWallpapers[safe: indexPath.row] else {
return owsFailDebug("Missing wallpaper for index \(indexPath.row)")
}
selectionHandler(wallpaper)
}
}
private class WallpaperCell: UICollectionViewCell {
static let reuseIdentifier = "WallpaperCell"
var wallpaperView: UIView?
var wallpaper: Wallpaper?
func configure(for wallpaper: Wallpaper, shouldDimInDarkMode: Bool) {
guard wallpaper != self.wallpaper else { return }
self.wallpaper = wallpaper
wallpaperView?.removeFromSuperview()
wallpaperView = Wallpaper.viewBuilder(
for: wallpaper,
customPhoto: { nil },
shouldDimInDarkTheme: shouldDimInDarkMode,
)?.build().asPreviewView()
guard let wallpaperView else {
return owsFailDebug("Missing wallpaper view")
}
contentView.addSubview(wallpaperView)
contentView.clipsToBounds = true
wallpaperView.autoPinEdgesToSuperviewEdges()
}
}