Extract preview body in the configuration

This commit is contained in:
Alexandr Goncharov 2020-10-27 10:02:31 +03:00
parent 3a0051ca0c
commit e3f915b2cb
6 changed files with 152 additions and 67 deletions

View File

@ -0,0 +1,8 @@
import SwiftUI
@available(iOS 13, *)
extension View {
func eraseToAny() -> AnyView {
AnyView(self)
}
}

View File

@ -11,55 +11,16 @@ struct CatalogItem<Content: UICatalogPresentable>: View {
ForEach(values: Content.previewModels) { model in
ForEach(values: configuration.colorSchemes) { scheme in
ForEach(values: configuration.contentSizeCategory) { category in
item(model: model,
scheme: scheme,
category: category,
size: configuration.size)
configuration.itemStyle
.body(content: Content.preview(with: model),
configuration: .init(modelInfo: String(describing: model),
scheme: scheme,
category: category,
size: configuration.size))
}
}
}
}
func item(model: Content.PreviewModel,
scheme: ColorScheme,
category: ContentSizeCategory,
size: CGSize?) -> some View {
VStack(alignment: .center, spacing: 0) {
HStack {
Image(systemName: scheme.systemImageName)
Image(systemName: category.systemImageName)
Text(String(describing: model))
.lineLimit(4)
.frame(maxWidth: 300, alignment: .leading)
}
.padding()
Content.preview(with: model)
.modifier(SizeModifier(size: size))
.frame(maxWidth: .infinity)
.padding()
.background(Color(.systemBackground))
.colorScheme(scheme)
.environment(\.sizeCategory, category)
}
.background(Color(.systemGroupedBackground))
.cornerRadius(6)
.padding()
}
}
@available(iOS 13, *)
private struct SizeModifier: ViewModifier {
let size: CGSize?
func body(content: Content) -> some View {
guard let size = size else {
return AnyView(content)
}
return AnyView(content.frame(width: size.width,
height: size.height))
}
}
#if DEBUG

View File

@ -6,31 +6,47 @@ import UIKit
extension UICatalog {
/// Configuration which will be used for preview generation
public struct PreviewConfiguration {
/// Creates new instance
/// - Parameters:
/// - themes: list of themes
/// - contentSize: list of content sizes
/// - size: required size
public init(themes: [Theme] = Theme.allCases,
contentSize: [UIContentSizeCategory] = [.unspecified],
size: CGSize? = nil) {
self.themes = themes
self.contentSize = contentSize
self.size = size
}
let themes: [Theme]
let contentSize: [UIContentSizeCategory]
let size: CGSize?
let itemStyle: PreviewItemStyle
}
}
/// Adjusts the size of the generated preview
/// - Parameter newSize: required size
/// - Returns: updated configuration
public func with(size newSize: CGSize) -> PreviewConfiguration {
PreviewConfiguration(themes: themes, contentSize: contentSize, size: newSize)
}
extension UICatalog.PreviewConfiguration {
/// Creates new instance
/// - Parameters:
/// - themes: list of themes
/// - contentSize: list of content sizes
/// - size: required size
public init(themes: [UICatalog.Theme] = UICatalog.Theme.allCases,
contentSize: [UIContentSizeCategory] = [.unspecified],
size: CGSize? = nil) {
self.themes = themes
self.contentSize = contentSize
self.size = size
itemStyle = UICatalog.DefaultPreviewItemStyle()
}
/// Adjusts the size of the generated preview
/// - Parameter newSize: required size
/// - Returns: updated configuration
public func with(size newSize: CGSize) -> UICatalog.PreviewConfiguration {
UICatalog.PreviewConfiguration(themes: themes, contentSize: contentSize, size: newSize)
}
}
extension UICatalog.PreviewConfiguration {
@available(iOS 13, *)
public func with(itemStyle: PreviewItemStyle) -> UICatalog.PreviewConfiguration {
UICatalog.PreviewConfiguration(themes: self.themes,
contentSize: self.contentSize,
size: self.size,
itemStyle: self.itemStyle)
}
}
extension UICatalog {
/// Represents color scheme
public enum Theme: CaseIterable {
case light, dark

View File

@ -38,7 +38,7 @@ public extension UICatalog.PreviewDescriptor {
title: String? = nil) where Content: UIViewCatalogPresentable {
id = "\(content)"
self.title = title ?? "\(content)"
builder = { AnyView(CatalogItem<Content>(configuration: configuration)) }
builder = { CatalogItem<Content>(configuration: configuration).eraseToAny() }
}
/// Group multiple previews together
@ -59,6 +59,6 @@ public extension UICatalog.PreviewDescriptor {
id = content.map(\.id).joined()
self.title = title ?? content.map(\.title).joined(separator: " ")
let items = content.map { GroupItem.Model(title: $0.title, content: $0.builder) }
builder = { AnyView(GroupItem(items: items)) }
builder = { GroupItem(items: items).eraseToAny() }
}
}

View File

@ -0,0 +1,100 @@
import SwiftUI
@available(iOS 13, *)
public protocol PreviewItemStyle {
/// Creates a view that represents single item of a preview.
/// - Parameters:
/// - content: preview content
/// - configuration: the properties of the preview.
func body(content: AnyView, configuration: Self.Configuration) -> AnyView
/// The properties of a preview.
typealias Configuration = UICatalog.PreviewItemStyleConfiguration
}
extension UICatalog {
@available(iOS 13, *)
public struct PreviewItemStyleConfiguration {
public let modelInfo: String
public let scheme: ColorScheme
public let category: ContentSizeCategory
public let size: CGSize?
}
@available(iOS 13, *)
struct DefaultPreviewItemStyle: PreviewItemStyle {
func body(content: AnyView, configuration: Configuration) -> AnyView {
UICatalog.DefaultPreviewItemStyleView(content: content,
configuration: configuration)
.eraseToAny()
}
}
@available(iOS 13, *)
struct DefaultPreviewItemStyleView: View {
let content: AnyView
let configuration: PreviewItemStyleConfiguration
var body: some View {
VStack(alignment: .center, spacing: 0) {
HStack {
Image(systemName: configuration.scheme.systemImageName)
Image(systemName: configuration.category.systemImageName)
Text(configuration.modelInfo)
.lineLimit(4)
.frame(maxWidth: 300, alignment: .leading)
}
.padding()
content
.modifier(SizeModifier(size: configuration.size))
.frame(maxWidth: .infinity)
.padding()
.background(Color(.systemBackground))
.colorScheme(configuration.scheme)
.environment(\.sizeCategory, configuration.category)
}
.background(Color(.systemGroupedBackground))
.cornerRadius(6)
.padding()
}
}
}
@available(iOS 13, *)
private struct SizeModifier: ViewModifier {
let size: CGSize?
func body(content: Content) -> some View {
guard let size = size else {
return content.eraseToAny()
}
return content.frame(width: size.width,
height: size.height)
.eraseToAny()
}
}
#if DEBUG
@available(iOS 13, *)
struct DefaultPreviewItemStyle_Previews: PreviewProvider {
static var previews: some View {
ScrollView(.vertical) {
UICatalog.DefaultPreviewItemStyleView(content: content, configuration: light)
UICatalog.DefaultPreviewItemStyleView(content: content, configuration: dark)
}
}
static var content: AnyView {
Text("Hello").background(Color.red)
.eraseToAny()
}
static let light = UICatalog.PreviewItemStyleConfiguration(modelInfo: "Info", scheme: .light,
category: .medium, size: nil)
static let dark = UICatalog.PreviewItemStyleConfiguration(modelInfo: "Info", scheme: .dark,
category: .medium, size: nil)
}
#endif

View File

@ -25,7 +25,7 @@ public protocol UICatalogPresentable {
@available(iOS 13, *)
extension UICatalogPresentable {
public static func preview(with model: PreviewModel) -> some View {
public static func preview(with model: PreviewModel) -> AnyView {
makePreviewInstance().preview(with: model)
}
}