Created
May 18, 2021 21:09
-
-
Save marcosgriselli/eb4b6f076fcc440259f34f8bbcfd8209 to your computer and use it in GitHub Desktop.
Simulate side-by-side Xcode Previews
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
PreviewsPlus.Canvas( | |
content: ContentView(), | |
previews: [ | |
Preview(device: .iPhoneSE, colorScheme: .light, contentSizeCategory: .extraExtraExtraLarge), | |
Preview(device: .iPhone12Pro, colorScheme: .dark), | |
Preview(device: .iPhone12Pro, contentSizeCategory: .accessibilityExtraLarge, layoutDirection: .rightToLeft), | |
Preview(device: .iPhone12Pro, colorScheme: .dark, contentSizeCategory: .extraSmall, layoutDirection: .rightToLeft) | |
] | |
) | |
} | |
} | |
struct Canvas<Content: View>: View { | |
var content: Content | |
var previews: [Preview] | |
init(content: Content, previews: [Preview]) { | |
self.content = content | |
self.previews = previews | |
} | |
var body: some View { | |
HStack(spacing: 32) { | |
ForEach(0..<previews.count) { index in | |
content | |
.ifLet(previews[index].colorScheme) { | |
$0.environment(\.colorScheme, $1) | |
} | |
.ifLet(previews[index].sizeCategory) { | |
$0.environment(\.sizeCategory, $1) | |
} | |
.ifLet(previews[index].layoutDirection) { | |
$0.environment(\.layoutDirection, $1) | |
} | |
.environment(\.horizontalSizeClass, previews[index].device.horizontalSizeClass) | |
.environment(\.verticalSizeClass, previews[index].device.verticalSizeClass) | |
.environment(\.displayScale, previews[index].device.displayScale) | |
.frame(width: previews[index].device.size.width, height: previews[index].device.size.height) | |
} | |
} | |
.previewLayout(.sizeThatFits) | |
} | |
} | |
struct Device { | |
let name: String | |
let size: CGSize | |
let horizontalSizeClass: UserInterfaceSizeClass | |
let verticalSizeClass: UserInterfaceSizeClass | |
let userInterfaceIdiom: UIUserInterfaceIdiom | |
let orientation: UIInterfaceOrientation | |
let safeArea: UIEdgeInsets | |
let displayScale: CGFloat | |
static let iPhoneSE = Device( | |
name: "iPhone SE (1st Gen)", | |
size: CGSize(width: 320, height: 568), | |
horizontalSizeClass: .compact, | |
verticalSizeClass: .regular, | |
userInterfaceIdiom: .phone, | |
orientation: .portrait, | |
safeArea: .init(top: 20, left: 0, bottom: 0, right: 0), | |
displayScale: 2 | |
) | |
static let iPhone12Pro = Device( | |
name: "iPhone 12 Pro", | |
size: CGSize(width: 390, height: 844), | |
horizontalSizeClass: .compact, | |
verticalSizeClass: .regular, | |
userInterfaceIdiom: .phone, | |
orientation: .portrait, | |
safeArea: .init(top: 44, left: 0, bottom: 34, right: 0), | |
displayScale: 3 | |
) | |
static let iPadPro12_9 = Device( | |
name: "iPad Pro 12.9-inch", | |
size: CGSize(width: 1024, height: 1366), | |
horizontalSizeClass: .regular, | |
verticalSizeClass: .regular, | |
userInterfaceIdiom: .pad, | |
orientation: .portrait, | |
safeArea: .init(top: 20, left: 0, bottom: 0, right: 0), | |
displayScale: 2 | |
) | |
} | |
struct Preview { | |
let device: Device | |
let colorScheme: ColorScheme? | |
let sizeCategory: ContentSizeCategory? | |
let layoutDirection: LayoutDirection? | |
internal init(device: PreviewsPlus.Device, colorScheme: ColorScheme? = nil, contentSizeCategory: ContentSizeCategory? = nil, layoutDirection: LayoutDirection? = nil) { | |
self.device = device | |
self.colorScheme = colorScheme | |
self.sizeCategory = contentSizeCategory | |
self.layoutDirection = layoutDirection | |
} | |
} | |
extension View { | |
@ViewBuilder func ifLet<Content: View, T>(_ value: T?, transform: (Self, T) -> Content) -> some View { | |
if let value = value { | |
transform(self, value) | |
} else { | |
self | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment