View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

更多视频

大多数浏览器和
Developer App 均支持流媒体播放。

  • 概览
  • 转写文稿
  • 代码
  • 在 Xcode Library 中添加自定义视图

    Xcode Library 是查看可用 SwiftUI 视图和将视图拖拽到 Xcode 预览画布的便捷方式,也能够对你的 app 进行丰富的可视化编辑。我们将向你展示如何用自己的视图和修正来扩展 Xcode Library 里的内容,如何在 app 或 Swift 包中进行优化从而提高可重用性和可发现性。 关于 Xcode 预览的更多内容,请查看 “为 SwiftUI 预览架构你的 app” 和 “SwiftUI 视图的可视化编辑”。

    资源

      • 高清视频
      • 标清视频

    相关视频

    WWDC20

    • 为 SwiftUI 预览组织你的 app
    • 视觉编辑 SwiftUI 视图
  • 下载

    ♪ Hello and welcome to WWDC.

    Anton Vladimirov: Hello, and welcome to WWDC.

    My name is Anton, and in this session we're going to take some time to talk about your SwiftUI views and modifiers inside Xcode.

    Since the very beginning, Xcode Previews was built to put your content front and center.

    For example, your views are previewable without doing any extra work, and most views and modifiers are inspectable right out of the box as well.

    In Xcode 12, we are taking a step further by letting you add your SwiftUI views and modifiers to the Xcode library.

    Let's take a step back and talk about some benefits of the Xcode library.

    First, discoverability.

    As your code base grows, it can become hard for the users of your code to know exactly what the key parts of your API are.

    The library is a place where developers look to discover available visual content, and by placing your most important and most reusable views there, you make them easier to find.

    But discoverability is just the beginning.

    Since each asset in the library comes in the compilable form, it can serve as an excellent way of educating the users of your code about how any particular view or modifier was intended to be used with a clear example that can often serve as an excellent starting point.

    And finally, every asset in the library can be dragged and dropped into the canvas, and inserting views and modifiers from the library allows Xcode previews to continue rendering, enabling rich visual editing for your content.

    So now, let's look at what you need to do to expose your content in the library.

    And this is where the library takes a page from Xcode preview's playbook.

    Since the view or the modifier you're adding to the library is defined in Swift code, the most natural place to define how to expose it in the library is also in Swift code, right next to the view or the modifier itself.

    The big advantage of doing that is that the compiler can help us ensure that the library items remain functional as your API changes.

    To extend the Xcode library, we need to declare a new type that conforms to the LibraryContentProvider protocol.

    This protocol has two requirements: a views property, which you can use to extend the view's Xcode library; and the modifiers function, which you can use to extend the modifiers Xcode library.

    There are some differences between the two which we will discuss in a moment.

    But for now, notice that both of them return an array of LibraryItems.

    Let's look at how to create a LibraryItem.

    The minimum amount of data that Xcode needs to create a LibraryItem is a completion that will get inserted when the user picks that item from the library.

    But you can also optionally specify additional information such as that library item's visibility, a more descriptive title for the item, or its category.

    There's a lot to unpack here.

    And the best way to discover this API is with a demo.

    So let's take a look.

    So I am here in the Fruta app that my team has been working on for a while.

    This is an app that allows the user to explore different kinds of smoothies.

    And in particular, I'm looking at a SmoothieRowView which is a view designed to showcase a particular smoothie with information such as its title, its ingredients, its image, and so on.

    This view is used in a couple of places in our app, and so I'd like to add it to the library to make the reuse easier, and I'm going to do that now.

    I'm going to scroll to the bottom of this file and add a new type that conforms to the LibraryContentProvider protocol.

    This type can be named anything but for the purposes of this demo, I'm going to call it LibraryContent.

    Since what I'm adding is a view, I'm going to add a views property.

    And now I need to add a LibraryItem.

    To finish completing the LibraryItem, I need to provide a completion that represents my view.

    Since what I'm adding is a SmoothieRowView, I'm just going to instantiate a SmoothieRowView here.

    I'm going to provide some default data here for my smoothie.

    I'm going to pick out of the list of preexisting ones.

    And I like lemonberry, so I'm just going to pick lemonberry here.

    And that's really all we need to create a LibraryItem for this view.

    I'm ready to use it.

    I'm going to switch to the SmoothieList.

    As the name implies, this is just a view that takes an array of smoothies and shows them in a navigable list.

    It's empty right now, as you can see from the preview.

    I'm going to populate it with my SmoothieRowView.

    To do that, I'm going to click on the library icon in the toolbar, which is a little plus, and I'm going to search for smoothie.

    As you can see, Xcode already discovered my LibraryItem and populated the library with it.

    It even gave it a category, Fruta, which matches the name of my project.

    This makes local items from my project easier to find.

    It also gave it a rich title, Smoothie Row View, which is based on the completion I provided.

    So now I can just drag this library item right into my code and my preview will update automatically to reflect the change that I just made.

    Notice that all of the rows in this list right now are saying "Lemonberry," and it's because Xcode inserted exactly what I specified with a lemonberry placeholder.

    But there's a slight difference here.

    The argument for the SmoothieRowView here, lemonberry, that I provided, is tokenized, and that's because LibraryItems are meant to be a starting point that can be customized based on the insertion context for the view that you're inserting.

    So I'm going to select the lemonberry token here, and I'm going to replace it with the data from the list, in this case, just smoothie.

    So the preview updates to show me the content of the list with the different smoothies provided from its preview.

    That's exactly what I want.

    So now let's talk about customization options for these LibraryItems.

    And for that, I'm going to go back to the LibraryContent struct that I'm working on.

    First, let's talk about the category.

    Now the project category is very useful for projects that are small and add a small number of items, such as this demo.

    But for a larger project that adds a lot of items, this category will become pretty unwieldy pretty quickly.

    If you've ever used SwiftUI's Xcode library, you notice that SwiftUI deals with that by adding categories that correspond to functionality such as controls, layout, effects, and so on.

    We can do the same for our LibraryItems as well.

    I'm going to go back to my code, and I'm going to add an argument here to specify the category.

    And I'm going to pick control because this view is most like control; it provides data and allows me to interact with it.

    Now if I bring up the library with a Command-Shift-L shortcut and search for smoothie, you will see that the category of this LibraryItem was updated.

    It's still prepended with Fruta, so it's still easy for me to find the local ones.

    But now it's clearly indicated that my view is a control.

    The icon for the LibraryItem changed as well, to indicate that it's a control with the color blue.

    So views don't have to correspond to LibraryItems one-to-one.

    It is perfectly reasonable to create multiple LibraryItems representing the same view in different configurations.

    In this particular case, SmoothieRowView has another argument that lets me specify whether I want to see local popularity of a particular smoothie.

    I'm going to add a LibraryItem for that configuration as well.

    I'm going to go back to my views property and add another LibraryItem.

    And it also creates a SmoothieRowView, but now it also uses showNearbyPopularity flag and sets its default to true.

    If I bring up my library again, I can see that my LibraryItem got added just like I would expect, but there's a problem.

    It's really hard for me to tell which LibraryItem represents which instantiation.

    I can fix that by adding another argument to this LibraryItem instantiation that specifies its title.

    In this case, I'm going to say, "Smoothie Row View With Popularity." And while I'm here, I can also fix its category to match the other SmoothieRowView as well.

    Now if I bring up the library, I can see that my LibraryItem got updated, and it's much easier for me to tell which one is which.

    So that's a quick intro in how to add a view to the Xcode library.

    But what about modifiers? Turns out the idea is very similar but has a little bit of a detail.

    Let me set the stage here for a second.

    As I audited my code, I found out that a lot of times when we use an image, we also use these three modifiers in a row: resizable, aspectRatio, and frame.

    And the result of these modifiers is to resize an image to a particular size while keeping its aspect ratio.

    We use it enough that I actually separated this functionality into its own modifier.

    I created an extension on Image, and I created a function called resizedtoFill, which takes the width and height.

    And this function just takes the image and applies the three modifiers to it and returns the result.

    I'd like to add resizedToFill to the modifiers library.

    To do that, I'm going to implement the second requirement of the LibraryContentProvider protocol, the modifiers function.

    And this is very similar to the views property, but it requires a base argument.

    When figuring out what the completion for the LibraryItem should be, Xcode needs a way to be able to tell which part of the completion is the modifier and which part is the thing it modifies.

    And the base is the way for us to communicate that.

    So since my modifier is declared on Image, I'm going to set this type to Image.

    And then I'm going to add my LibraryItem.

    Its completion is base with resizedToFill called on it.

    And I just need to populate this with sample data.

    And just like that, my modifier item is ready to be used.

    I'm going to scroll back to my image and delete the three modifiers I'm trying to replace.

    I'm going to bring up the library again, but this time I'm going to switch to the modifiers tab and search for resized.

    And here's my new modifier.

    I can use it right now by hitting Return, and what gets inserted is exactly what I would expect.

    Xcode stripped the base part and just inserted the modifier itself and tokenized the arguments so I can customize them here with size, and size, like it was before.

    And just like that, I used my modifier from the library.

    You may have noticed that at no point during this demo, we had to build or run our project to populate the library.

    This is because Xcode can harvest the library definitions by simply scanning your source for LibraryContentProviders and parsing their declarations.

    There are several advantages to this.

    First, it means that if your project is not in a runnable state, you can still contribute content to the library which is really handy when you're in the middle of that UI rework.

    Your project doesn't run, but you still want the content of the library.

    It also means that there's no additional build configuration required to enable this feature.

    And since LibraryContentProvider code is never actually executed, the compiler will strip it when your project is built for distribution.

    So this approach works really well for building a library of content for your workspace or project.

    But since Xcode scans all of the source files in your workspace for library content, including any dependencies you have, it actually works really well with Swift packages as well.

    In our project, we actually have a dependency on the Nutrition Facts Swift package, which provides facilities for visualizing nutritional information.

    I'm looking for a view that will allow me to display caloric information of a smoothie in my row view.

    I can use the library to explore the content of Nutrition Facts package for a view I want.

    I'm going to bring up the library again, but this time I'm going to switch back to the views tab and scroll down until I find the Nutrition Facts category.

    This is a category that Xcode created for the content of the Nutrition Facts Swift package.

    I can see that there's a CalorieCountView here which sounds like exactly what I want.

    I'm going to hit Return to insert it, and my preview will update to reflect the change that I just made.

    This basically looks exactly like what I want, so all I need to do now is to populate this view with the data from my model.

    Fortunately, I already have nutrition facts, so I can just type smoothie.nutritionFact here and have my view updated.

    And just like that, I was able to discover and use content from a Swift package without ever opening its source code.

    To summarize, in this session we've learned how to extend the Xcode library by creating a type that conforms to the LibraryContentProvider protocol, implementing one or both of its requirements, and returning instances of LibraryItem that correspond to the individual items.

    Thank you, and have a great WWDC.

    • 1:57 - LibraryContentProvider

      public protocol LibraryContentProvider {
        @LibraryContentBuilder
        var views: [LibraryItem] { get }
      
        @LibraryContentBuilder
        public func modifiers(base: ModifierBase) -> [LibraryItem]
      }
    • 2:32 - LibraryItem

      LibraryItem(
        SmoothieRowView(smoothie: .lemonberry),
        visible: true,
        title: "Smoothie Row View",
        category: .control
      )
    • 3:22 - LibraryContent

      struct LibraryContent: LibraryContentProvider {
          @LibraryContentBuilder
          var views: [LibraryItem] {
              LibraryItem(
                  SmoothieRowView(smoothie: .lemonberry),
                  category: .control
              )
              
              LibraryItem(
                  SmoothieRowView(smoothie: .lemonberry, showNearbyPopularity: true),
                  title: "Smoothie Row View With Popularity",
                  category: .control
              )
          }
      }
    • 8:57 - Image extension

      extension Image {
          func resizedToFill(width: CGFloat, height: CGFloat) -> some View {
              return self
                  .resizable()
                  .aspectRatio(contentMode: .fill)
                  .frame(width: width, height: height)
          }
      }
    • 9:17 - Modifiers

      @LibraryContentBuilder
          func modifiers(base: Image) -> [LibraryItem] {
              LibraryItem(
                  base.resizedToFill(width: 100.0, height: 100.0)
              )
          }
  • 正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。

    提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。

Developer Footer

  • 视频
  • WWDC20
  • 在 Xcode Library 中添加自定义视图
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则