Merge pull request #1 from 0111b/dev

Preview styling
This commit is contained in:
Alexandr Goncharov 2020-10-19 10:49:58 +03:00 committed by GitHub
commit def4a7aa48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 224 additions and 56 deletions

24
.vscode/launch.json vendored Normal file
View File

@ -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"
}
]
}

23
.vscode/tasks.json vendored Normal file
View File

@ -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"
]
}
}

View File

@ -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"])
]
)

View File

@ -9,4 +9,3 @@ extension Binding {
return Binding(get: { value }, set: { value = $0 })
}
}

View File

@ -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, *)

View File

@ -38,4 +38,3 @@ struct ComponentPreview<Component: View>: View {
}
}
}

View File

@ -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

View File

@ -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")) })
])
}
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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: " ")

View File

@ -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"
}
}
}

View File

@ -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() }
}

View File

@ -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)
}