【发布时间】:2020-09-01 01:34:30
【问题描述】:
UIView 具有readableContentGuide,因此布局可能与屏幕大小相关,并确保内容始终可读。我在 SwiftUI 中找不到关于这些东西的任何信息。
在 SwiftUI 中可以使用什么等效的东西?如果没有,我们可以使用 SwiftUI 组件在 UIKit 中构建相同的效果吗?
谢谢。
【问题讨论】:
UIView 具有readableContentGuide,因此布局可能与屏幕大小相关,并确保内容始终可读。我在 SwiftUI 中找不到关于这些东西的任何信息。
在 SwiftUI 中可以使用什么等效的东西?如果没有,我们可以使用 SwiftUI 组件在 UIKit 中构建相同的效果吗?
谢谢。
【问题讨论】:
在 SwiftUI 中没有直接的 readableContentGuide 替代方案。它应该直接支持到视图中。从理论上讲,SwiftUI 应该应用设备特定的填充,但它不会在 v1.0 中发生。因此,即,将 padding 修饰符添加到 TextField 应该在分辨率中应用更大的填充,例如 12.9 英寸的 iPad。
即便如此,您也可以编写自己的视图修饰符来应用自定义填充。这是@mecid 完成的示例:
import SwiftUI
private struct ReadableGuidePadding: ViewModifier {
@Environment(\.horizontalSizeClass) var horizontal
func body(content: Content) -> some View {
content.padding(.horizontal, horizontal == .regular ? 84: 16)
}
}
extension View {
func readableGuidePadding() -> some View {
modifier(ReadableGuidePadding())
}
}
【讨论】:
其他解决方案对我不起作用,因为它们依赖于硬编码的填充量或下降到 UIKit,这对于需要在所有不同尺寸的 iPhone 上看起来都很好的多平台 SwiftUI 应用程序来说不是很好、iPad 和 Mac。因此,由于仍然没有原生的 SwiftUI 等价物(从 iOS 15/macOS 12 开始),我想我会做一个替代品。
我的替代方法是根据以下公式计算填充量:
struct ReadabilityPadding: ViewModifier {
let isEnabled: Bool
@ScaledMetric private var unit: CGFloat = 20
func body(content: Content) -> some View {
// Use a GeometryReader here to get view width.
GeometryReader { geometryProxy in
content
.padding(.horizontal, padding(for: geometryProxy.size.width))
}
}
private func padding(for width: CGFloat) -> CGFloat {
guard isEnabled else { return 0 }
// The internet seems to think the optimal readable width is 50-75
// characters wide; I chose 70 here. The `unit` variable is the
// approximate size of the system font and is wrapped in
// @ScaledMetric to better support dynamic type. I assume that
// the average character width is half of the size of the font.
let idealWidth = 70 * unit / 2
// If the width is already readable then don't apply any padding.
guard width >= idealWidth else {
return 0
}
// If the width is too large then calculate the padding required
// on either side until the view's width is readable.
let padding = round((width - idealWidth) / 2)
return padding
}
}
这是它在不同设备上的外观(顶部是正常的,底部是应用了 ReadabilityPadding 视图修饰符):
【讨论】:
考虑到设备宽度的无限范围,此处建议的填充解决方案没有意义。我们需要一个解决方案来满足所有类型的设备尺寸,并且可以为内容保留可读的宽度。
任何视图都可以以 SwiftUI 友好的方式使用最终结果:
SomeReallyLongView()
.readableGuidePadding()
首先,我们需要一个ViewModifier,它可以将视图的宽度固定到最大边距。 UIKit 实际上将可读内容宽度定义为 672,所以让我们使用它。
private struct ReadableGuidePadding: ViewModifier {
func body(content: Content) -> some View {
HStack(spacing: 0) {
Spacer(minLength: 0)
content.frame(maxWidth: 672)
Spacer(minLength: 0)
}
}
}
现在我们有了一个修饰符,可以将我们的内容裁剪为 672 点宽,并在需要时使用水平分隔符。请注意,我们使用 0 的 minLength 否则在较小的屏幕上总是会有水平填充。我们不希望这样。
现在我们可以创建一个方便的视图扩展来使用我们的修饰符:
extension View {
func readableGuidePadding() -> some View {
modifier(ReadableGuidePadding())
}
}
享受
【讨论】:
这应该可行:
private struct ReadablePadding: ViewModifier {
private static let readableAera: CGSize = UIViewController().view.readableContentGuide.layoutFrame.size
@State var orientation = UIDevice.current.orientation
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.verticalSizeClass) var verticalSizeClass
func body(content: Content) -> some View {
content
.padding(.horizontal, padding())
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
orientation = UIDevice.current.orientation
}
}
private func padding() -> CGFloat {
var width = horizontalSizeClass == .compact ? min(UIScreen.main.bounds.width, UIScreen.main.bounds.height) :
max(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
if horizontalSizeClass == .regular, verticalSizeClass == .regular { // iPads
print(orientation)
width = UIScreen.main.bounds.width
}
return (width - ReadablePadding.readableAera.width) / 2 + 16.0 // 16.0 is a margin
}
}
extension View {
func readableGuidePadding() -> some View {
modifier(ReadablePadding())
}
}
【讨论】:
.doubleMargin?