如何在 SwiftUI 中将 print() 打印到 Xcode 控制台?
How to print() to Xcode console in SwiftUI?
所以我尝试在 SwiftUI 视图中调试时放置打印语句。
print("landmark: \(landmark)")
在下面的正文中。
var body: some View {
NavigationView {
List {
Toggle(isOn: $userData.showFavoritesOnly) {
Text("Favorite only")
}
ForEach(landmarkData) { landmark in
print("landmark: \(landmark)")
if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
编译器错误:
那么,在 SwiftUI 中打印到控制台的正确方法是什么?
编辑:
我让 Landmark 符合 CustomStringConvertible:
struct Landmark: Hashable, Codable, Identifiable, CustomStringConvertible {
var description: String { name+"\(id)" }
var id: Int
var name: String
.....
我仍然收到 "String is not convertible to any" 错误。现在应该可以了吗?
你不能,因为你在计算 属性 中。例如,您需要一个按钮,并在您定义打印的操作中。或者使用断点
您不能在主体结构中打印,即 某些视图的结构 type.For 打印您需要从主体结构中创建函数并使用按钮或调用它别的。
尝试右键单击实时预览播放按钮并从弹出窗口中选择“调试预览”
可以使用 print() 记住所有 SwiftUI View 内容都是 (a) 隐式闭包和 (b) 强烈建议尽可能分解视图以获得简单的结构,所以它可能看起来像以下...
struct Model: Identifiable {
let value: String
var id: String {
value
}
init (_ value: String) {
self.value = value
}
}
struct TestView: View {
@State var showFavoritesOnly = false
@State var listData: [Model] = [Model("one"), Model("two"), Model("three")]
var body: some View {
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorite only")
}
ForEach(listData) { data in
self.rowView(data: data)
}
}
}
}
private func rowView(data: Model) -> some View {
#if DEBUG
print(">> \(data.value)")
#endif
return NavigationLink(destination: Text("Details")) {
Text("Go next from \(data.value)")
}
}
}
... 并在预览中右键单击 select 运行 作为调试预览,我们得到:
2019-10-31 14:28:03.467635+0200 Test[65344:11155167] [Agent] Received connection, creating agent
2019-10-31 14:28:04.472314+0200 Test[65344:11155168] [Agent] Received display message
>> one
>> two
>> three
这是一个助手 Print( ... )
视图,它的作用类似于 print( ... )
函数,但在视图
中
将其放入您的任何视图文件中
extension View {
func Print(_ vars: Any...) -> some View {
for v in vars { print(v) }
return EmptyView()
}
}
并像这样在 body
内部使用
Print("Here I am", varOne, varTwo ...)
或像这样 ForEach {}
里面
self.Print("Inside ForEach", varOne, varTwo ...)
注意:与现有视图合并时,您可能需要将 Print()
放入 Group {}
中
您可以在主体结构中打印,但要这样做,您必须明确 return 您要呈现的视图。通常在 SwiftUI 中,主体 属性 隐式 return 是视图。例如,这将在您尝试打印时抛出错误:
struct SomeView: View {
@State var isOpen = false
var body: some View {
print(isOpen) // error thrown here
VStack {
// other view code
|
}
}
但是如果我们明确地 return 我们想要的视图那么它将起作用,例如
struct SomeView: View {
@State var isOpen = false
var body: some View {
print(isOpen) // this ok because we explicitly returned the view below
// Notice the added 'return' below
return VStack {
// other view code
}
}
}
如果你想在return查看你的视图之前查看状态或环境对象是如何变化的,上面的方法会很有效,但如果你想在视图中打印更深层次的东西,你正在尝试return,那么我会选择@Rok Krulec 的回答。
您可以轻松地在函数构建器中的任何位置添加打印语句,只需将其 return 值存储在通配符中,有效地忽略它:
let _ = print("hi!")
无需设置或其他冗长的内容!
为什么这行得通而常规 print()
不行?
SwiftUI 的 @ViewBuilder
(以及一般的结果生成器)的方式是它们使用闭包中的任何值,这些值在其他情况下不使用(例如,如果您只有 42
在它自己的行). print
函数 returns Void
(无),构建器必须将其构建到视图中,因此它失败了。通过将它分配给一个变量(在本例中 _
,基本上是一个您永远无法访问的变量),Void
永远不会首先提供给视图构建器。
您可能会争辩说构建器应该简单地接受并忽略 Void
值,但想法是您的构建器闭包不应该有副作用(我也会在完成调试后删除 print
语句)—你不应该依赖于在特定时间调用这些闭包。
调试预览的非常简单的方法:
- Open your Swift project in Xcode 11.
- Right-click (or Control-click) on the Live Preview button in the bottom right corner of the preview.
- Select Debug Preview.
How to debug your SwiftUI previews in Xcode
可以概括为:
extension View {
func Perform(_ block: () -> Void) -> some View {
block()
return EmptyView()
}
}
所以在你的例子中:
ForEach(landmarkData) { landmark in
Perform { print("landmark: \(landmark)") }
if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
给你。它就像简单的打印一样工作,但在视图中。
func printv( _ data : Any)-> EmptyView{
print(data)
return EmptyView()
}
并像那样使用它
struct ContentView: View {
var body: some View {
VStack() {
Text("hello To SwiftUI")
printv("its easy to code in SwiftUI")
Text("And Good to have you here")
}
}
}
// 试试这个,在视图上添加一个 'return' 然后 'print' 可以存活。
struct ContentView: View {
var num: Int = 1
var body: some View {
print(num)
return Text("hello")
}
}
View
上的以下扩展与 print
一样直观,因为它复制了默认的 print(_:separator:terminator:)
函数签名和行为。
extension View {
func printUI(_ args: Any..., separator: String = " ", terminator: String = "\n") -> EmptyView {
let output = args.map(String.init(describing:)).joined(separator: separator)
print(output, terminator: terminator)
return EmptyView()
}
}
用法示例:
struct ContentView: View {
var body: some View {
VStack {
printUI("ContentView", "1")
printUI("ContentView", "2", separator: ", ", terminator: "\n.\n.\n")
printUI("ContentView", "3", separator: "; ")
Text("Hello, World!")
}
}
}
控制台输出:
ContentView 1
ContentView, 2
.
.
ContentView; 3
这应该有效
if true {
print(aVar, "xx")
}
return ZStack {
...
}
在 SwiftUI 视图中调试时最安全、最简单的打印方式。
extension View {
func Print(_ item: Any) -> some View {
#if DEBUG
print(item)
#endif
return self
}
}
用法示例:
struct ContentView: View {
var body: some View {
VStack {
ForEach((1...5), id: \.self) { number in
Text("\(number)")
.Print(number)
}
}
}
}
控制台输出:
1
2
3
4
5
所以我尝试在 SwiftUI 视图中调试时放置打印语句。
print("landmark: \(landmark)")
在下面的正文中。
var body: some View {
NavigationView {
List {
Toggle(isOn: $userData.showFavoritesOnly) {
Text("Favorite only")
}
ForEach(landmarkData) { landmark in
print("landmark: \(landmark)")
if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
编译器错误:
那么,在 SwiftUI 中打印到控制台的正确方法是什么?
编辑: 我让 Landmark 符合 CustomStringConvertible:
struct Landmark: Hashable, Codable, Identifiable, CustomStringConvertible {
var description: String { name+"\(id)" }
var id: Int
var name: String
.....
我仍然收到 "String is not convertible to any" 错误。现在应该可以了吗?
你不能,因为你在计算 属性 中。例如,您需要一个按钮,并在您定义打印的操作中。或者使用断点
您不能在主体结构中打印,即 某些视图的结构 type.For 打印您需要从主体结构中创建函数并使用按钮或调用它别的。
尝试右键单击实时预览播放按钮并从弹出窗口中选择“调试预览”
可以使用 print() 记住所有 SwiftUI View 内容都是 (a) 隐式闭包和 (b) 强烈建议尽可能分解视图以获得简单的结构,所以它可能看起来像以下...
struct Model: Identifiable {
let value: String
var id: String {
value
}
init (_ value: String) {
self.value = value
}
}
struct TestView: View {
@State var showFavoritesOnly = false
@State var listData: [Model] = [Model("one"), Model("two"), Model("three")]
var body: some View {
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorite only")
}
ForEach(listData) { data in
self.rowView(data: data)
}
}
}
}
private func rowView(data: Model) -> some View {
#if DEBUG
print(">> \(data.value)")
#endif
return NavigationLink(destination: Text("Details")) {
Text("Go next from \(data.value)")
}
}
}
... 并在预览中右键单击 select 运行 作为调试预览,我们得到:
2019-10-31 14:28:03.467635+0200 Test[65344:11155167] [Agent] Received connection, creating agent
2019-10-31 14:28:04.472314+0200 Test[65344:11155168] [Agent] Received display message
>> one
>> two
>> three
这是一个助手 Print( ... )
视图,它的作用类似于 print( ... )
函数,但在视图
将其放入您的任何视图文件中
extension View {
func Print(_ vars: Any...) -> some View {
for v in vars { print(v) }
return EmptyView()
}
}
并像这样在 body
内部使用
Print("Here I am", varOne, varTwo ...)
或像这样 ForEach {}
里面
self.Print("Inside ForEach", varOne, varTwo ...)
注意:与现有视图合并时,您可能需要将 Print()
放入 Group {}
中
您可以在主体结构中打印,但要这样做,您必须明确 return 您要呈现的视图。通常在 SwiftUI 中,主体 属性 隐式 return 是视图。例如,这将在您尝试打印时抛出错误:
struct SomeView: View {
@State var isOpen = false
var body: some View {
print(isOpen) // error thrown here
VStack {
// other view code
|
}
}
但是如果我们明确地 return 我们想要的视图那么它将起作用,例如
struct SomeView: View {
@State var isOpen = false
var body: some View {
print(isOpen) // this ok because we explicitly returned the view below
// Notice the added 'return' below
return VStack {
// other view code
}
}
}
如果你想在return查看你的视图之前查看状态或环境对象是如何变化的,上面的方法会很有效,但如果你想在视图中打印更深层次的东西,你正在尝试return,那么我会选择@Rok Krulec 的回答。
您可以轻松地在函数构建器中的任何位置添加打印语句,只需将其 return 值存储在通配符中,有效地忽略它:
let _ = print("hi!")
无需设置或其他冗长的内容!
为什么这行得通而常规 print()
不行?
SwiftUI 的 @ViewBuilder
(以及一般的结果生成器)的方式是它们使用闭包中的任何值,这些值在其他情况下不使用(例如,如果您只有 42
在它自己的行). print
函数 returns Void
(无),构建器必须将其构建到视图中,因此它失败了。通过将它分配给一个变量(在本例中 _
,基本上是一个您永远无法访问的变量),Void
永远不会首先提供给视图构建器。
您可能会争辩说构建器应该简单地接受并忽略 Void
值,但想法是您的构建器闭包不应该有副作用(我也会在完成调试后删除 print
语句)—你不应该依赖于在特定时间调用这些闭包。
调试预览的非常简单的方法:
- Open your Swift project in Xcode 11.
- Right-click (or Control-click) on the Live Preview button in the bottom right corner of the preview.
- Select Debug Preview.
How to debug your SwiftUI previews in Xcode
可以概括为:
extension View {
func Perform(_ block: () -> Void) -> some View {
block()
return EmptyView()
}
}
所以在你的例子中:
ForEach(landmarkData) { landmark in
Perform { print("landmark: \(landmark)") }
if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationButton(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
给你。它就像简单的打印一样工作,但在视图中。
func printv( _ data : Any)-> EmptyView{
print(data)
return EmptyView()
}
并像那样使用它
struct ContentView: View {
var body: some View {
VStack() {
Text("hello To SwiftUI")
printv("its easy to code in SwiftUI")
Text("And Good to have you here")
}
}
}
// 试试这个,在视图上添加一个 'return' 然后 'print' 可以存活。
struct ContentView: View {
var num: Int = 1
var body: some View {
print(num)
return Text("hello")
}
}
View
上的以下扩展与 print
一样直观,因为它复制了默认的 print(_:separator:terminator:)
函数签名和行为。
extension View {
func printUI(_ args: Any..., separator: String = " ", terminator: String = "\n") -> EmptyView {
let output = args.map(String.init(describing:)).joined(separator: separator)
print(output, terminator: terminator)
return EmptyView()
}
}
用法示例:
struct ContentView: View {
var body: some View {
VStack {
printUI("ContentView", "1")
printUI("ContentView", "2", separator: ", ", terminator: "\n.\n.\n")
printUI("ContentView", "3", separator: "; ")
Text("Hello, World!")
}
}
}
控制台输出:
ContentView 1
ContentView, 2
.
.
ContentView; 3
这应该有效
if true {
print(aVar, "xx")
}
return ZStack {
...
}
在 SwiftUI 视图中调试时最安全、最简单的打印方式。
extension View {
func Print(_ item: Any) -> some View {
#if DEBUG
print(item)
#endif
return self
}
}
用法示例:
struct ContentView: View {
var body: some View {
VStack {
ForEach((1...5), id: \.self) { number in
Text("\(number)")
.Print(number)
}
}
}
}
控制台输出:
1
2
3
4
5