使用 SwiftUI 定义 macOS window 大小
Define macOS window size using SwiftUI
我正在使用 SwiftUI 开发新的 macOS 应用程序,但不知道如何定义 window 大小。在AppDelegate中,window大小定义如下:
// --- AppDelegate.swift ---
import Cocoa
import SwiftUI
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
}
}
ContentView定义如下:
// --- ContentView.swift ---
import SwiftUI
struct ContentView : View {
var body: some View {
VStack {
Spacer()
Text("Top Text")
Spacer()
Text("Bottom Text")
Spacer()
}
}
}
当我构建和 运行 应用程序时,window 出现但大小不正确。它显示为两个文本标签的大小,这不是 AppDelegate 中定义的 480x300 大小。
使用 SwiftUI 时,我应该如何为 Mac 应用程序定义 window 大小?
有时,这种行为可能会令人困惑。这是因为一旦您 运行 您的应用程序至少一次,如果您随后手动调整大小并重新定位您的 window,委托中指定的大小将不再重要。
应用程序会记住用户何时调整了 window 的大小,并将使用存储在 UserDefaults 中的信息,该信息位于键 "NSWindow Frame Main Window" 下。如果您想重置它,您需要使用 defaults 命令将其清除。
既然那已经不存在了,那么您的 window 如此狭窄的原因如下:
使用 SwiftUI 时,并非所有视图都是平等创建的。例如:Text() 是谦虚的。只需要 space 就够了。而其他视图,例如 Spacer(),将尽可能扩展它们的父项(我称它们为贪心)。
在你的例子中,你有一个 VStack,里面有 Spacer()。这意味着 VStack 填充扩展以填充其父级提供的高度。在这种情况下,来自委托的 300 pt(或存储在 UserDefaults 中的任何内容)。
另一方面,由于 HStack 中没有任何 Spacer(),ContentView 只会水平扩展到它需要的位置。也就是说,与最宽的 Text() 视图一样宽。如果您在 VStack 中添加 HStack { Spacer() }
,您的内容视图 将 扩展以占据委托中指定的 480 pt(或任何内容)存储在 UserDefaults 中)。无需设置 frame().
另一种方法(为 ContentView 指定框架)基本上是告诉您的 ContentView 为 480x300,无论如何。事实上,如果您这样做,您将无法调整 window!
的大小
所以现在你知道了,我认为这很清楚......但是,这里有一些对你非常有用的东西:
还有另一个贪心视图可以帮助您调试 window 尺寸:GeometryReader。这种观点总是有多少就有多少。 运行 这个例子,你就会知道应用启动时提供了多少space:
struct ContentView : View {
var body: some View {
GeometryReader { geometry in
VStack {
Text("\(geometry.size.width) x \(geometry.size.height)")
}.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
我写了一篇关于 GeometryReader 的广泛文章,我建议您查看:https://swiftui-lab.com/geometryreader-to-the-rescue/
顺便说一下,我的 AppDelegate 看起来像这样:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
}
更新(macOS Catalina - Beta 3)
从Beta3开始,新项目的初始ContentView使用了maxWidth和maxHeight。一个聪明的选择。
struct ContentView : View {
var body: some View {
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
我确实是这样做的。它有启动大小和可调整大小。
struct windowSize {
// changes let to static - read comments
static minWidth : CGFloat = 100
static minHeight : CGFloat = 200
static maxWidth : CGFloat = 200
static maxHeight : CGFloat = 250
}
struct ContentView : View {
var body: some View {
Group() {
VStack {
Text("Hot Stuff")
.border(Color.red, width: 1)
Text("Hot Chocolate")
.border(Color.red, width: 1)
}
}
.frame(minWidth: windowSize().minWidth, minHeight: windowSize().minHeight)
.frame(maxWidth: windowSize().maxWidth, maxHeight: windowSize().maxHeight)
.border(Color.blue, width: 1)
}
}
SwiftUI
您可以在主结构中轻松使用(编译器指令)
在我的例子中,我用它来检测哪个屏幕应用程序是 运行,因为我正在开发一个多平台应用程序(iPhone、iPad、mac)
import SwiftUI
@main
struct DDStoreApp: App {
var body: some Scene {
WindowGroup {
// use compiler directives #if to detect mac version or ios version
#if os(macOS)
// use min wi & he to make the start screen 800 & 1000 and make max wi & he to infinity to make screen expandable when user stretch the screen
ContentView().frame(minWidth: 800, maxWidth: .infinity, minHeight: 1000, maxHeight: .infinity, alignment: .center)
#else
ContentView()
#endif
}
}
}
我正在使用 SwiftUI 开发新的 macOS 应用程序,但不知道如何定义 window 大小。在AppDelegate中,window大小定义如下:
// --- AppDelegate.swift ---
import Cocoa
import SwiftUI
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
}
}
ContentView定义如下:
// --- ContentView.swift ---
import SwiftUI
struct ContentView : View {
var body: some View {
VStack {
Spacer()
Text("Top Text")
Spacer()
Text("Bottom Text")
Spacer()
}
}
}
当我构建和 运行 应用程序时,window 出现但大小不正确。它显示为两个文本标签的大小,这不是 AppDelegate 中定义的 480x300 大小。
使用 SwiftUI 时,我应该如何为 Mac 应用程序定义 window 大小?
有时,这种行为可能会令人困惑。这是因为一旦您 运行 您的应用程序至少一次,如果您随后手动调整大小并重新定位您的 window,委托中指定的大小将不再重要。
应用程序会记住用户何时调整了 window 的大小,并将使用存储在 UserDefaults 中的信息,该信息位于键 "NSWindow Frame Main Window" 下。如果您想重置它,您需要使用 defaults 命令将其清除。
既然那已经不存在了,那么您的 window 如此狭窄的原因如下:
使用 SwiftUI 时,并非所有视图都是平等创建的。例如:Text() 是谦虚的。只需要 space 就够了。而其他视图,例如 Spacer(),将尽可能扩展它们的父项(我称它们为贪心)。
在你的例子中,你有一个 VStack,里面有 Spacer()。这意味着 VStack 填充扩展以填充其父级提供的高度。在这种情况下,来自委托的 300 pt(或存储在 UserDefaults 中的任何内容)。
另一方面,由于 HStack 中没有任何 Spacer(),ContentView 只会水平扩展到它需要的位置。也就是说,与最宽的 Text() 视图一样宽。如果您在 VStack 中添加 HStack { Spacer() }
,您的内容视图 将 扩展以占据委托中指定的 480 pt(或任何内容)存储在 UserDefaults 中)。无需设置 frame().
另一种方法(为 ContentView 指定框架)基本上是告诉您的 ContentView 为 480x300,无论如何。事实上,如果您这样做,您将无法调整 window!
的大小所以现在你知道了,我认为这很清楚......但是,这里有一些对你非常有用的东西:
还有另一个贪心视图可以帮助您调试 window 尺寸:GeometryReader。这种观点总是有多少就有多少。 运行 这个例子,你就会知道应用启动时提供了多少space:
struct ContentView : View {
var body: some View {
GeometryReader { geometry in
VStack {
Text("\(geometry.size.width) x \(geometry.size.height)")
}.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
我写了一篇关于 GeometryReader 的广泛文章,我建议您查看:https://swiftui-lab.com/geometryreader-to-the-rescue/
顺便说一下,我的 AppDelegate 看起来像这样:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
}
更新(macOS Catalina - Beta 3)
从Beta3开始,新项目的初始ContentView使用了maxWidth和maxHeight。一个聪明的选择。
struct ContentView : View {
var body: some View {
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
我确实是这样做的。它有启动大小和可调整大小。
struct windowSize {
// changes let to static - read comments
static minWidth : CGFloat = 100
static minHeight : CGFloat = 200
static maxWidth : CGFloat = 200
static maxHeight : CGFloat = 250
}
struct ContentView : View {
var body: some View {
Group() {
VStack {
Text("Hot Stuff")
.border(Color.red, width: 1)
Text("Hot Chocolate")
.border(Color.red, width: 1)
}
}
.frame(minWidth: windowSize().minWidth, minHeight: windowSize().minHeight)
.frame(maxWidth: windowSize().maxWidth, maxHeight: windowSize().maxHeight)
.border(Color.blue, width: 1)
}
}
SwiftUI
您可以在主结构中轻松使用(编译器指令) 在我的例子中,我用它来检测哪个屏幕应用程序是 运行,因为我正在开发一个多平台应用程序(iPhone、iPad、mac)
import SwiftUI
@main
struct DDStoreApp: App {
var body: some Scene {
WindowGroup {
// use compiler directives #if to detect mac version or ios version
#if os(macOS)
// use min wi & he to make the start screen 800 & 1000 and make max wi & he to infinity to make screen expandable when user stretch the screen
ContentView().frame(minWidth: 800, maxWidth: .infinity, minHeight: 1000, maxHeight: .infinity, alignment: .center)
#else
ContentView()
#endif
}
}
}