diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..61444f6 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f87aa7e --- /dev/null +++ b/.vscode/tasks.json @@ -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" + ] + } +} \ No newline at end of file diff --git a/Package.swift b/Package.swift index c00f004..dc6844c 100644 --- a/Package.swift +++ b/Package.swift @@ -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"]) ] ) diff --git a/Sources/UIPreview/Extensions/Binding+Mock.swift b/Sources/UIPreview/Extensions/Binding+Mock.swift index 6d98cd8..dc08373 100644 --- a/Sources/UIPreview/Extensions/Binding+Mock.swift +++ b/Sources/UIPreview/Extensions/Binding+Mock.swift @@ -9,4 +9,3 @@ extension Binding { return Binding(get: { value }, set: { value = $0 }) } } - diff --git a/Sources/UIPreview/Preview/ColorSchemePreview.swift b/Sources/UIPreview/Preview/ColorSchemePreview.swift index 6648129..6d0282d 100644 --- a/Sources/UIPreview/Preview/ColorSchemePreview.swift +++ b/Sources/UIPreview/Preview/ColorSchemePreview.swift @@ -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, *) diff --git a/Sources/UIPreview/Preview/ComponentPreview.swift b/Sources/UIPreview/Preview/ComponentPreview.swift index 4153758..39d8a6e 100644 --- a/Sources/UIPreview/Preview/ComponentPreview.swift +++ b/Sources/UIPreview/Preview/ComponentPreview.swift @@ -38,4 +38,3 @@ struct ComponentPreview: View { } } } - diff --git a/Sources/UIPreview/UICatalog/CatalogItem.swift b/Sources/UIPreview/UICatalog/CatalogItem.swift index 69d00aa..5d1002e 100644 --- a/Sources/UIPreview/UICatalog/CatalogItem.swift +++ b/Sources/UIPreview/UICatalog/CatalogItem.swift @@ -3,41 +3,55 @@ import SwiftUI #endif import UIKit -@available(iOS 13, *) +// TODO: remove UIView dependency + +@available(iOS 14, *) struct CatalogItem: 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(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 diff --git a/Sources/UIPreview/UICatalog/GroupItem.swift b/Sources/UIPreview/UICatalog/GroupItem.swift index 0c81215..cdd3458 100644 --- a/Sources/UIPreview/UICatalog/GroupItem.swift +++ b/Sources/UIPreview/UICatalog/GroupItem.swift @@ -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")) }) ]) } } diff --git a/Sources/UIPreview/UICatalog/Preview.swift b/Sources/UIPreview/UICatalog/Preview.swift index 01ee785..c073425 100644 --- a/Sources/UIPreview/UICatalog/Preview.swift +++ b/Sources/UIPreview/UICatalog/Preview.swift @@ -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) } diff --git a/Sources/UIPreview/UICatalog/PreviewConfiguration.swift b/Sources/UIPreview/UICatalog/PreviewConfiguration.swift index 6c3ef87..2a5b702 100644 --- a/Sources/UIPreview/UICatalog/PreviewConfiguration.swift +++ b/Sources/UIPreview/UICatalog/PreviewConfiguration.swift @@ -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 + } } diff --git a/Sources/UIPreview/UICatalog/PreviewDescriptor.swift b/Sources/UIPreview/UICatalog/PreviewDescriptor.swift index dd6ce42..6444060 100644 --- a/Sources/UIPreview/UICatalog/PreviewDescriptor.swift +++ b/Sources/UIPreview/UICatalog/PreviewDescriptor.swift @@ -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.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: " ") diff --git a/Sources/UIPreview/UICatalog/PreviewLegend.swift b/Sources/UIPreview/UICatalog/PreviewLegend.swift new file mode 100644 index 0000000..737744d --- /dev/null +++ b/Sources/UIPreview/UICatalog/PreviewLegend.swift @@ -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" + } + } +} diff --git a/Sources/UIPreview/UICatalog/UICatalogPresentable.swift b/Sources/UIPreview/UICatalog/UICatalogPresentable.swift index 944b6f0..06be1a1 100644 --- a/Sources/UIPreview/UICatalog/UICatalogPresentable.swift +++ b/Sources/UIPreview/UICatalog/UICatalogPresentable.swift @@ -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() } } diff --git a/Sources/UIPreview/UIKitWrapper/UIViewWrapper.swift b/Sources/UIPreview/UIKitWrapper/UIViewWrapper.swift index 0257518..bb21b3b 100644 --- a/Sources/UIPreview/UIKitWrapper/UIViewWrapper.swift +++ b/Sources/UIPreview/UIKitWrapper/UIViewWrapper.swift @@ -8,7 +8,7 @@ public struct UIViewWrapper: UIViewRepresentable { let contextBuilder: () -> ViewCoordinator public init(_ builder: @autoclosure @escaping () -> ContentView, - update: @escaping (ContentView) -> Void = { _ in }) { + update: @escaping (ContentView) -> Void = { _ in }) { contextBuilder = { ViewCoordinator(build: builder, update: update) }