如何在 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 语句)—你不应该依赖于在特定时间调用这些闭包。

调试预览的非常简单的方法:

  1. Open your Swift project in Xcode 11.
  2. Right-click (or Control-click) on the Live Preview button in the bottom right corner of the preview.
  3. 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