commit
def4a7aa48
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
// Running executables
|
||||
// Running unit tests
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug tests on macOS",
|
||||
"program": "/Applications/Xcode.app/Contents/Developer/usr/bin/xctest",
|
||||
"args": [
|
||||
"${workspaceFolder}/.build/debug/${workspaceFolderBasename}PackageTests.xctest"
|
||||
],
|
||||
"preLaunchTask": "swift-build-tests"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug tests on Linux",
|
||||
"program": "${workspaceFolder}/.build/x86_64-unknown-linux/debug/${workspaceFolderBasename}PackageTests.xctest",
|
||||
"preLaunchTask": "swift-build-tests"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
// compile your SPM project
|
||||
{
|
||||
"label": "swift-build",
|
||||
"type": "shell",
|
||||
"command": "swift build"
|
||||
},
|
||||
// compile your SPM tests
|
||||
{
|
||||
"label": "swift-build-tests",
|
||||
"type": "process",
|
||||
"command": "swift",
|
||||
"group": "build",
|
||||
"args": [
|
||||
"build",
|
||||
"--build-tests",
|
||||
"--enable-test-discovery"
|
||||
// for TensorFlow add "-Xlinker", "-ltensorflow"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ let package = Package(
|
|||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "UIPreview",
|
||||
targets: ["UIPreview"]),
|
||||
targets: ["UIPreview"])
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
|
@ -26,6 +26,6 @@ let package = Package(
|
|||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "UIPreviewTests",
|
||||
dependencies: ["UIPreview"]),
|
||||
dependencies: ["UIPreview"])
|
||||
]
|
||||
)
|
||||
|
|
|
@ -9,4 +9,3 @@ extension Binding {
|
|||
return Binding(get: { value }, set: { value = $0 })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,14 @@ extension ColorScheme {
|
|||
var previewName: String {
|
||||
String(describing: self).capitalized
|
||||
}
|
||||
|
||||
var systemImageName: String {
|
||||
switch self {
|
||||
case .dark: return "sun.max.fill"
|
||||
case .light: return "sun.max"
|
||||
@unknown default: return "questionmark"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
|
|
|
@ -38,4 +38,3 @@ struct ComponentPreview<Component: View>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,41 +3,55 @@ import SwiftUI
|
|||
#endif
|
||||
import UIKit
|
||||
|
||||
@available(iOS 13, *)
|
||||
// TODO: remove UIView dependency
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct CatalogItem<Content: UIViewCatalogPresentable>: View {
|
||||
let configuration: UICatalog.PreviewConfiguration
|
||||
|
||||
var body: some View {
|
||||
ForEach(values: Content.previewModels) { model in
|
||||
ForEach(values: configuration.colorSchemes) { scheme in
|
||||
item(model: model, scheme: scheme)
|
||||
ForEach(values: configuration.contentSizeCategory) { category in
|
||||
item(model: model, scheme: scheme, category: category)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(model: Content.PreviewModel,
|
||||
scheme: ColorScheme) -> some View {
|
||||
VStack(alignment: .center, spacing: 12.0) {
|
||||
Text("\(scheme.previewName): \(String(describing: model))")
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
scheme: ColorScheme,
|
||||
category: ContentSizeCategory) -> some View {
|
||||
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: scheme.systemImageName)
|
||||
Image(systemName: category.systemImageName)
|
||||
Text(String(describing: model))
|
||||
.frame(maxWidth: 300)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Content.preview(with: model)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color(.systemBackground))
|
||||
.colorScheme(scheme)
|
||||
Divider()
|
||||
.background(Color.secondary)
|
||||
.environment(\.sizeCategory, category)
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.cornerRadius(6)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
struct CatalogItem_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
ScrollView(.vertical) {
|
||||
PreviewLegend()
|
||||
CatalogItem<TestView>(configuration: .init())
|
||||
}.navigationBarTitle("TestView")
|
||||
}
|
||||
|
@ -47,11 +61,15 @@ struct CatalogItem_Previews: PreviewProvider {
|
|||
private final class TestView: UILabel, UICatalogPresentable {
|
||||
static var previewModels = [
|
||||
"Hello world",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"
|
||||
"Hello2 world",
|
||||
"Hello3 world",
|
||||
"Hello4 world"
|
||||
]
|
||||
|
||||
func apply(previewModel: String) {
|
||||
text = previewModel
|
||||
textColor = .systemRed
|
||||
numberOfLines = 0
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
import SwiftUI
|
||||
#endif
|
||||
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
struct GroupItem: View {
|
||||
struct Model: Identifiable {
|
||||
let id = UUID().uuidString
|
||||
let id = UUID().uuidString // swiftlint:disable:this identifier_name
|
||||
var isExpanded = false
|
||||
let title: String
|
||||
let content: () -> AnyView
|
||||
|
@ -20,8 +20,7 @@ struct GroupItem: View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
struct GroupItemRow: View {
|
||||
|
||||
@State var model: GroupItem.Model
|
||||
|
@ -31,42 +30,37 @@ struct GroupItemRow: View {
|
|||
Button(action: {
|
||||
model.isExpanded.toggle()
|
||||
}, label: {
|
||||
HStack {
|
||||
Text(model.title)
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
if model.isExpanded {
|
||||
Image(systemName: "chevron.up")
|
||||
} else {
|
||||
Image(systemName: "chevron.down")
|
||||
}
|
||||
}
|
||||
.offset(x: 10)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color.secondary)
|
||||
.cornerRadius(8)
|
||||
Label(model.title,
|
||||
systemImage: model.isExpanded
|
||||
? "chevron.up"
|
||||
: "chevron.down")
|
||||
})
|
||||
if model.isExpanded {
|
||||
model.content()
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding([.top, .bottom], /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.cornerRadius(6)
|
||||
.padding()
|
||||
|
||||
}
|
||||
if model.isExpanded {
|
||||
model.content()
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding([.top, .bottom], 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
struct GroupItem_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ScrollView {
|
||||
PreviewLegend()
|
||||
GroupItem(items: [
|
||||
.init(title: "Group 1",
|
||||
content: { AnyView(Text("Preview")) } ),
|
||||
content: { AnyView(Text("Preview")) }),
|
||||
.init(title: "Group 2",
|
||||
content: { AnyView(Image(systemName: "square.and.pencil")) } )
|
||||
content: { AnyView(Image(systemName: "square.and.pencil")) })
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import SwiftUI
|
|||
import UIKit
|
||||
|
||||
extension UICatalog {
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
public struct Preview {
|
||||
init(_ view: AnyView, title: String) {
|
||||
self.title = title
|
||||
|
@ -13,6 +13,7 @@ extension UICatalog {
|
|||
|
||||
func preview() -> some View {
|
||||
ScrollView(.vertical, showsIndicators: true) {
|
||||
PreviewLegend()
|
||||
view
|
||||
}.navigationBarTitle(title)
|
||||
}
|
||||
|
|
|
@ -5,11 +5,14 @@ import UIKit
|
|||
|
||||
extension UICatalog {
|
||||
public struct PreviewConfiguration {
|
||||
public init(themes: [Theme] = Theme.allCases) {
|
||||
public init(themes: [Theme] = Theme.allCases,
|
||||
contentSize: [UIContentSizeCategory] = [.unspecified]) {
|
||||
self.themes = themes
|
||||
self.contentSize = contentSize
|
||||
}
|
||||
|
||||
let themes: [Theme]
|
||||
let contentSize: [UIContentSizeCategory]
|
||||
}
|
||||
|
||||
public enum Theme: CaseIterable {
|
||||
|
@ -27,7 +30,11 @@ extension UICatalog.Theme {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
extension UICatalog.PreviewConfiguration {
|
||||
var colorSchemes: [ColorScheme] { themes.map(\.scheme) }
|
||||
var contentSizeCategory: [ContentSizeCategory] {
|
||||
let categories = contentSize.compactMap { ContentSizeCategory($0) }
|
||||
return categories.isEmpty ? [.medium] : categories
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ import SwiftUI
|
|||
import UIKit
|
||||
|
||||
extension UICatalog {
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
public struct PreviewDescriptor: Identifiable, Hashable {
|
||||
let builder: () -> AnyView
|
||||
public let id: String
|
||||
public let id: String // swiftlint:disable:this identifier_name
|
||||
public let title: String
|
||||
public var preview: Preview { Preview(builder(), title: title) }
|
||||
|
||||
|
@ -22,7 +22,7 @@ extension UICatalog {
|
|||
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
@available(iOS 14, *)
|
||||
public extension UICatalog.PreviewDescriptor {
|
||||
init<Content>(_ content: Content.Type,
|
||||
configuration: UICatalog.PreviewConfiguration = .init(),
|
||||
|
@ -33,13 +33,11 @@ public extension UICatalog.PreviewDescriptor {
|
|||
}
|
||||
|
||||
init(_ content: UICatalog.PreviewDescriptor...,
|
||||
configuration: UICatalog.PreviewConfiguration = .init(),
|
||||
title: String? = nil) {
|
||||
self.init(content, configuration: configuration, title: title)
|
||||
self.init(content, title: title)
|
||||
}
|
||||
|
||||
init(_ content: [UICatalog.PreviewDescriptor],
|
||||
configuration: UICatalog.PreviewConfiguration = .init(),
|
||||
title: String? = nil) {
|
||||
id = content.map(\.id).joined()
|
||||
self.title = title ?? content.map(\.title).joined(separator: " ")
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
#endif
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct PreviewLegend: View {
|
||||
@State var isExpanded: Bool = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Button(action: {
|
||||
isExpanded.toggle()
|
||||
}, label: {
|
||||
Label("Legend",
|
||||
systemImage: isExpanded
|
||||
? "chevron.up"
|
||||
: "chevron.down")
|
||||
.foregroundColor(.primary)
|
||||
})
|
||||
if isExpanded {
|
||||
PreviewLegendBody()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.cornerRadius(6)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
private struct PreviewLegendBody: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Divider()
|
||||
.foregroundColor(.accentColor)
|
||||
Text("Theme: ")
|
||||
.font(.headline)
|
||||
.padding()
|
||||
ForEach(values: UICatalog.Theme.allCases) { theme in
|
||||
Label {
|
||||
Text(theme.legend)
|
||||
} icon: {
|
||||
Image(systemName: theme.scheme.systemImageName)
|
||||
}
|
||||
}
|
||||
Text("Content size: ")
|
||||
.font(.headline)
|
||||
.padding()
|
||||
ForEach(values: ContentSizeCategory.allCases) { category in
|
||||
Label {
|
||||
Text(String(describing: category))
|
||||
} icon: {
|
||||
Image(systemName: category.systemImageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct PreviewLegend_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
PreviewLegend()
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
}
|
||||
|
||||
private extension UICatalog.Theme {
|
||||
var legend: String {
|
||||
switch self {
|
||||
case .light: return "Light theme"
|
||||
case .dark: return "Dark theme"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
extension ContentSizeCategory {
|
||||
var systemImageName: String {
|
||||
switch self {
|
||||
case .accessibilityExtraExtraExtraLarge: return "7.circle.fill"
|
||||
case .accessibilityExtraExtraLarge: return "4.circle.fill"
|
||||
case .extraSmall: return "1.circle"
|
||||
case .small: return "2.circle"
|
||||
case .medium: return "3.circle"
|
||||
case .large: return "4.circle"
|
||||
case .extraLarge: return "5.circle"
|
||||
case .extraExtraLarge: return "6.circle"
|
||||
case .extraExtraExtraLarge: return "7.circle"
|
||||
case .accessibilityMedium: return "3.circle.fill"
|
||||
case .accessibilityLarge: return "4.circle.fill"
|
||||
case .accessibilityExtraLarge: return "5.circle.fill"
|
||||
@unknown default:
|
||||
return "questionmark"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ public protocol UICatalogPresentable {
|
|||
|
||||
public typealias UIViewCatalogPresentable = UIView & UICatalogPresentable
|
||||
|
||||
|
||||
@available(iOS 13, *)
|
||||
extension UICatalogPresentable where Self: UIView {
|
||||
public func preview(with model: PreviewModel) -> some View {
|
||||
|
@ -27,7 +26,6 @@ extension UICatalogPresentable where Self: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
extension UICatalogPresentable where Self: UIView {
|
||||
public static func makePreviewInstance() -> Self { self.init() }
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ public struct UIViewWrapper<ContentView: UIView>: UIViewRepresentable {
|
|||
let contextBuilder: () -> ViewCoordinator<ContentView>
|
||||
|
||||
public init(_ builder: @autoclosure @escaping () -> ContentView,
|
||||
update: @escaping (ContentView) -> Void = { _ in }) {
|
||||
update: @escaping (ContentView) -> Void = { _ in }) {
|
||||
contextBuilder = {
|
||||
ViewCoordinator(build: builder, update: update)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue