内容简介:This question has split the Apple developer community for years. Could this year’s WWDC finally mark the end of that?Last year, Apple made a huge step forward in UI development with the introduction ofThis made it possible for a developer who prefers the v
What do you prefer: creating user interfaces visually or in code?
This question has split the Apple developer community for years. Could this year’s WWDC finally mark the end of that?
Last year, Apple made a huge step forward in UI development with the introduction of SwiftUI : For the first time, the visual approach and the coding approach spoke the same language. It doesn’t matter if you add a button in code or by dragging it to the design canvas – both representations are always in sync.
This made it possible for a developer who prefers the visual approach and another one who prefers the code approach to work on the same project without any conflict. Yet, one important thing was still missing: The ability to simply drag and drop your own views onto the canvas.
With WWDC 2020 , Apple has closed this gap – the feature is finally here! So let’s dive right in and see how to add your custom views in SwiftUI to the Xcode library!
Here’s our sample project that we’re going to refer to:
Step 1: Create Custom Views in SwiftUI
In order to add custom views to the library, we first need a project. In this example, we’re going to create a simple app showcasing all our team members in a neat list.
We start with creating a new custom view called Row
which we will later use for displaying an indiviual team member. It consists of a profile image, a title (for the team member’s name) and a subtitle (for the job description).
struct Row: View { var title: String var subtitle: String var image: Image var body: some View { HStack(spacing: 8) { image .resizable() .frame(width: 75, height: 75, alignment: .center) VStack(alignment: .leading, spacing: 4) { Text(title) Text(subtitle) .opacity(0.5) } .font(.headline) Spacer() } .padding(8) } }
This view simply adds several (sub)views and lays them out the way we want. But what’s still missing is the “card look”: the rounded rectangle frame around it and the subtle shadow.
To keep our Row
view highly reusable and customizable, we don’t add the code for that to the view itself. Instead, we create a view modifier .card()
for that which we can apply not only to a row, but to other views as well.
struct Card: ViewModifier { let cornerRadius: CGFloat func body(content: Content) -> some View { content .background(Color(.systemBackground)) .cornerRadius(cornerRadius) .shadow(color: Color.primary.opacity(0.2), radius: 4) } } extension View { func card(cornerRadius: CGFloat = 8) -> some View { modifier(Card(cornerRadius: cornerRadius)) } }
With these components, we can now build a TeamMemberCell
view, which combines the two things together:
struct TeamMemberCell: View { let teamMember: TeamMember var body: some View { Row(title: teamMember.name, subtitle: teamMember.position, image: teamMember.image) .card(cornerRadius: 8) } }
Finally, we create a TeamList
which creates a TeamMemberCell
for each of our team members and arranges them in a vertical, scrollable stack. You can see how we did that in our sample project . We’ll skip that part in this article.
Step 2: Add Items to the Xcode View Library
All we’ve done so far isn’t new and could be accomplished with Xcode 11 as well. Now comes the cool part: Instead of composing the TeamMemberCell
in code, we want to be able to assemble it with drag & drop in the design canvas. Before we can do that, we first need to let Xcode know about the custom views and modifiers we want to add to the view library.
For that, the new API LibraryContentProvider was introduced in Xcode 12. The following example adds the Row
view and the .card()
modifier to the library, alongside another custom view AppButton
and a custom .loading
modifier. (Feel free to check out the code for the latter two in our sample project .)
struct ViewProvider: LibraryContentProvider { @LibraryContentBuilder var views: [LibraryItem] { LibraryItem(Row(title: "", subtitle: "", image: Image(""))) LibraryItem(AppButton(Text(""), action: {})) } func modifiers<V: View>(base: V) -> [LibraryItem] { [ LibraryItem(base.card(cornerRadius: 8)), LibraryItem(base.loading(false)) ] } }
As you can see, we need to make a type conform to the LibraryContentProvider
protocol to add new items to the Xcode library. However, we can’t just make our views and modifiers conform to that protocol (which we first assumed). Instead, we need to create a separate structure (which we called ViewProvider
) that implements this protocol. In its view
property, we return all our custom views that we want to add to the library, wrapped in a LibraryItem
. Similarly, we return all view modifiers that we want to have in the library from the modifiers
function.
Note:For the views
property, we use the LibraryContentBuilder
function builder similar to how ViewBuilder
can be used to add multiple views as children of a VStack
, HStack
or ZStack
. That’s why there are no array brackets around the library items.
In our ViewBuilder
example, we chose the easiest way to create each LibraryItem
by using the initializer with only a single parameter. That works, but we can also customize how that library item appears in Xcode by providing more context. Let’s take a closer look at the LibraryItem
initializer :
LibraryItem( Row(title: "", subtitle: "", image: Image("")), // the code to create the view visible: true, // whether it's visible in the Xcode library title: "My Awesome Team Member Row", // the custom name shown in the library category: .control, // a category to find you custom views faster matchingSignature: "" // the signature for code completion )
First of all, we can define what code snippet should be added to the SwiftUI code when using the LibraryItem
. We can further specify whether the item should only be visible for code completion or also appear when using the design canvas. Since the default value for the visible attribute is true
, we usually omit this parameter. It is also possible to define a custom title, as well as a category (including .effect
, .layout
, .control
and the default .other
). The category will be used for sorting the views and modifiers and also change their color in the Xcode library. A matching signature can also be added to determine which shortcut should trigger the code completion for the given modifier or view.
Step 3: Compose Views with Xcode 12
After building the project once, Row
, AppButton
, .card
and .loading
are available in the Xcode library, alongside all the native views! We can now use them to visually compose our TeamMemberCell
.
With the design canvas open, you can add any of our custom views and modifiers to it by clicking the [+] button and simply dragging the respective item over to the canvas.
Ain’t that cool?! :sunglasses:
Note: We noticed that the library items sometimes don’t show up in the Xcode library immediately. This is probably because Xcode 12 is still in Beta and will hopefully be resolved soon. If it doesn’t work for you right from the beginning, don’t give up! Quit and reopen Xcode, clean, build, change a little bit of code – and we promise it’ll work eventually. :wink:
Conclusion
By providing the same tooling for custom views and modifiers as provided for native components in addition to many other changes to the whole toolchain, SwiftUI finally feels like a first-class citizen when it comes to UI development on iOS. It was a long way to go, but in the year 2020, visual user interface development with Xcode is feature-equivalent to creating SwiftUI views in code.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。