Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions SurfPlaybook/Playbook/Core/Containers/SUIViewContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ extension SUIViewContainer: PlaybookContainer {
return self
}

public func makeSnapshot(with completion: @escaping (UIImage?) -> Void) {
DispatchQueue.main.async { [completion] in
completion(self.makeSnapshot())
}
}

public func makeSnapshot() -> UIImage? {
if #available(iOS 16, *) {
ImageRenderer(content: viewFactory().padding(12)).uiImage
Expand Down
11 changes: 11 additions & 0 deletions SurfPlaybook/Playbook/Core/Playbook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public final class Playbook {
/// Каждой главе будет соответствовать карусель на главном экране со страницами из этой главы.
@discardableResult
public func add(chapter: PlaybookChapter) -> Playbook {
PlaybookSnapshotsCache.shared.add(pages: chapter.pages)
chapters.append(chapter)
return self
}
Expand Down Expand Up @@ -81,4 +82,14 @@ public final class Playbook {
return self
}

/// Очищает все страницы и координаторы
/// - Note: Применимо, если вы встраиваете Playbook прямо в приложение.
/// Например, в Debug экран.
public func clear() {
chapters.removeAll()
uiKitPages.removeAll()
flowCoordinators.removeAll()
coordinator.removeAllDependencies()
}

}
2 changes: 1 addition & 1 deletion SurfPlaybook/Playbook/Core/PlaybookPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public struct PlaybookPage {
// и при необходимости просить контроллер
let config: PlaybookContainerProvider
var snapshot: UIImage? {
return config(nil).makeSnapshot()
return PlaybookSnapshotsCache.shared.get(page: self)
}

/// Предпочтительная конфигурация.
Expand Down
65 changes: 65 additions & 0 deletions SurfPlaybook/Playbook/Core/PlaybookSnapshotsCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// PlaybookSnapshotsCache.swift
// Pods
//
// Created by Nikita Korobeinikov on 16.04.2025.
//

import UIKit
import Foundation

final class PlaybookSnapshotsCache {

// MARK: - Snared

static var shared = PlaybookSnapshotsCache()

static let didUpdateNotification = Notification.Name("playbook-snapshots-updated")

/// Поток для синхронизации изменения кэша
private static let queue: DispatchQueue = .init(label: "PlaybookSnapshotsCache")

// MARK: - Private Properties

/// Кэш снапшотов
private var snapshots: [String: UIImage?] = [:] {
didSet {
NotificationCenter.default.post(
name: Self.didUpdateNotification,
object: nil
)
}
}

// MARK: - Internal Methods

func add(pages: [PlaybookPage]) {
for page in pages {
add(page: page)
}
}

func add(page: PlaybookPage) {
page.config(nil).makeSnapshot { snapshot in
Self.queue.sync { [weak self] in
self?.snapshots[page.snapshotKey] = snapshot
}

}
}

func get(page: PlaybookPage) -> UIImage? {
snapshots[page.snapshotKey] ?? .init()
}

}

// MARK: - Private Extensions

private extension PlaybookPage {

var snapshotKey: String {
"\(name)-\(description)"
}

}
11 changes: 7 additions & 4 deletions SurfPlaybook/Playbook/Core/Protocols/PlaybookContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Nikita Korobeinikov on 24.01.2025.
//

import Foundation
import UIKit

public typealias PlaybookContainerProvider = (UIViewController?) -> PlaybookContainer
Expand All @@ -20,17 +21,19 @@ public protocol PlaybookContainer where Self: UIView {
func loadView() -> UIView
/// Делает снимок вьюшки для отображения превьюшек
/// - Note: Перед снимком вызывает `loadView`
func makeSnapshot() -> UIImage?
func makeSnapshot(with completion: @escaping (UIImage?) -> Void)

}

// MARK: - Defaults

public extension PlaybookContainer {

func makeSnapshot() -> UIImage? {
loadView().layoutIfNeeded()
return snapshot()
func makeSnapshot(with completion: @escaping (UIImage?) -> Void) {
DispatchQueue.main.async { [completion] in
self.loadView().layoutIfNeeded()
completion(self.snapshot())
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ final class PageCollectionViewCell: UICollectionViewCell {
@IBOutlet private weak var snapshotImageView: UIImageView!
@IBOutlet private weak var includePresetsImageView: UIImageView!

// MARK: - Private Properties

private var model: PageModel?

// MARK: - UICollectionView

override func awakeFromNib() {
Expand All @@ -29,6 +33,10 @@ final class PageCollectionViewCell: UICollectionViewCell {
configureShadow()
}

deinit {
NotificationCenter.default.removeObserver(self)
}

override var isHighlighted: Bool {
didSet {
UIView.animate(withDuration: AnimationTime.tiny, delay: 0, options: .allowUserInteraction) {
Expand All @@ -50,19 +58,16 @@ final class PageCollectionViewCell: UICollectionViewCell {
titleLabel.apply(style: .textRegular14Black)
includePresetsImageView.isHidden = model.playbookPage.presets.count <= 1

guard let image = model.playbookPage.snapshot else {
snapshotImageView.image = nil
return
}
self.model = model

if
image.size.width > snapshotImageView.bounds.size.width
|| image.size.height > snapshotImageView.bounds.size.height {
snapshotImageView.contentMode = .scaleAspectFit
} else {
snapshotImageView.contentMode = .center
}
snapshotImageView.image = image
NotificationCenter.default.removeObserver(self)
NotificationCenter.default.addObserver(
self,
selector: #selector(tryUpdateImage),
name: PlaybookSnapshotsCache.didUpdateNotification,
object: nil
)
tryUpdateImage()
}

}
Expand Down Expand Up @@ -92,4 +97,25 @@ private extension PageCollectionViewCell {
containerView.layer.shadowOffset = .init(width: 0, height: 2)
}

func setImage(_ image: UIImage?) {
guard let image else {
snapshotImageView.image = nil
return
}

if
image.size.width > snapshotImageView.bounds.size.width
|| image.size.height > snapshotImageView.bounds.size.height {
snapshotImageView.contentMode = .scaleAspectFit
} else {
snapshotImageView.contentMode = .center
}
snapshotImageView.image = image
}

@objc
func tryUpdateImage() {
setImage(model?.playbookPage.snapshot)
}

}