Author: Jiang Yi (Tilan-Han)
Produced by: Alibaba’s new retail Taobao technology
Apple’s WWDC 2019 highlight will certainly be SwiftUI: new declarative syntax, binding apis, and responsive framework Combine. All of this heralds a revolution in The Apple Native layout system. Apple has worked on many fronts to make SwiftUI what it is today. Want to learn about Swift’s new features, SwiftUI data flow and SwiftUI Layout system? Take a look.
Swift 5.1 New syntax
Single-expression implicit return value
In the syntax prior to Swift 5.0, the return keyword could be omitted if a closure expression had only one expression. Now the same applies to evaluating attributes and function statements in Swift 5.1 and later. Such as:
Struct Rectangle {var width = 50, height = 50 var area1: Double {return width * height
}
func area2() -> Double {
returnStruct Rectangle {Rectangle width = 0.0px, Rectangle height = 0.0px; Rectangle width = 0.0px; Rectangle height = 0.0px; Double { width * height } func area2() -> Double { width * height } }Copy the code
A full proposal for this new feature can be found at github.com/apple/Swift…
The default initializer is synthesized from the default members of the structure
Prior to Swift 5.0 structure declarations, the compiler generated a member-by-member initializer by default and a no-parameter initializer if both had default values. However, if the structure member attributes are too many, and most of them have default values, then only the member-by-member initializer can be used, which will make the local writing method of each call too redundant. In traditional OOP language, Builder mode can be used to solve this problem, but after Swift 5.1, the compiler will synthesize initializer on demand. Avoid redundancy in initial writing. Such as:
struct Dog {
var name = "Generic dog name"
var age = 0
}
let boltNewborn = Dog()
let daisyNewborn = Dog(name: "Daisy", age: 0)
// before swift 5.0 ❎
let benjiNewborn = Dog(name: "Benji")
// after switft 5.1 ✅
let benjiNewborn = Dog(name: "Benji")
Copy the code
A full proposal for this new feature can be found at github.com/apple/Swift…
String insertion operator new design
This feature mainly expands the use of String insertion operators, previously used only in String initialization, but not in parameter processing. This syntax is used in the SwiftUI Text control, which allows you to initialize Tetx in a single line of expression.
// before swift 5.0 ❎
let quantity = 10
label.text = NSLocalizedString(
"You have \(quantity) apples, comment: "Number of apples") label.text = String(format: formatString, quantity) // after Switft 5.1 ✅ let quantity = 10 return text (."You have \(quantity) apples"). / /, in fact, the compiler will be translated into the following a few words of var builder. = LocalizedStringKey StringInterpolation (literalCapacity: 16, interpolationCount: 1 ) builder.appendLiteral("You have ")
builder.appendInterpolation(quantity)
builder.appendLiteral(" apples")
LocalizedStringKey(stringInterpolation: builder)
Copy the code
A full proposal for this new feature can be found at github.com/apple/Swift…
Attribute wrapper
When we declare calculated properties in a type, most of the properties are accessed and acquired for the same purpose. This code is extractable. For example, we mark some user preferences and proxy directly into the implementation of UserDefault in setting and obtaining calculated properties. We can modify this by declaring @propertyWarpper, which can reduce a lot of repetitive code. In SwiftUI, @state @enviromemntobjject @bindingobjject @Binding is proxying to the SwiftUI framework via property wrappers to automatically respond to changes in business State.
// before swift 5.0 ❎ struct User {static var usesTouchID: Bool {get {return UserDefaults.standard.bool(forKey: "USES_TOUCH_ID")}set {
UserDefaults.standard.set(newValue, forKey: "USES_TOUCH_ID")
}
}
static var isLoggedIn: Bool {
get {
return UserDefaults.standard.bool(forKey: "LOGGED_IN")}set {
UserDefaults.standard.set(newValue, forKey: "LOGGED_IN"// after Switft 5.1 ✅ @propertywrapper struct UserDefault<T> {let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
print("UserDefault init")
self.key = key
self.defaultValue = defaultValue
UserDefaults.standard.register(defaults: [key: defaultValue])
}
var value: T {
get {
print("getter")
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
print("setter")
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
struct User2 {
@UserDefault("USES_TOUCH_ID", defaultValue: false)
static var usesTouchID: Bool
@UserDefault("LOGGED_IN", defaultValue: false)
var isLoggedIn: Bool
}
print("hello world")
let user = User2()
User2.usesTouchID = true
let delegate = User2.$usesTouchID
print("\(delegate)")
let detelate2 = user.$isLoggedIn
Copy the code
The property wrapper is actually translated at compile time into the following code, and the compiler disallows the use of identifiers starting with $.
struct User2 {
static var $usesTouchID = UserDefault<Bool>("USES_TOUCH_ID", defaultValue: false)
static var usesTouchID: Bool {
set {
$usesTouchID.value = newValue
}
get {
$usesTouchID.value
}
}
@UserDefault("LOGGED_IN", defaultValue: false)
var isLoggedIn: Bool
}
Copy the code
In addition to reducing code duplication, Swift Runtime guarantees the following:
1. Property wrappers for instances are loaded on the fly
2. Attribute warrantors for class attributes are lazily loaded
Property wrappers are thread-safe
4. The original property wrapper instance is available via the $operator, which is heavily used in SwiftUI’s data dependencies
A full proposal for this new feature can be found at github.com/apple/swift…
Note: @PropertyDelegate and @PropertyWrapper are consistent in the current Beta release, and the official release should only retain one syntax.
Opaque return type
Before Swift 5.0 we used Generic Type or Protocol if we wanted to return abstract types. Using generics exposes some information to the API user, not the full Type abstraction. However, there are several limitations to using Protocol: the generic return value is a container at run time, which is inefficient, the return value cannot call a method of its own type, the Protocol is not allowed to have associated types, and the == operator cannot be used because the type information is lost at compile time and the compiler cannot infer the type.
Opaque Result Type is added in Swift 5.1. This feature uses some to modify the protocol return value and has the following features:
All conditional branches can return only one specific type, otherwise an error will be reported
2, method users still do not know the type, (user opaque)
3. The compiler knows the specific type, so it can use type inference.
// before Swift 5.0 ❎ public protocol View: _View {associatedType Body: View var Body: Self.Body { get } } // compile error func getText() -> View {return Text("")
}
func getText<T: Text>() -> T {
return Text(""} // after Switft 5.1 ✅ struct ContentView: View {var body: some View {Text("")}}Copy the code
A full proposal for this new feature can be found at github.com/apple/Swift…
Swift Style DSL / Function Builder
This proposal is a special one because it is still under review and has not been officially added to Swift. However, Swift 5.1 with Xcode11 already integrates this syntax. Using Function Builder allows expression statements to implicitly return in Function return values, which can be very expressive in DSLS with nested logic and return value expressions. The following statements are equivalent:
// Original source code:
@TupleBuilder
func build() -> (Int, Int, Int) {
1
2
3
}
// This code is interpreted exactly as if it were this code:
func build() -> (Int, Int, Int) {
let _a = 1
let _b = 2
let _c = 3
return TupleBuilder.buildBlock(_a, _b, _c)
}
Copy the code
Almost all layout class controls in SwiftUI use the Function Builder feature and are much more readable than Flutter’s DSL syntax. Here is an example of apple’s WWDC Session.
head {
meta().charset("UTF-8")
if cond {
title("Title 1")}else {
title("Title 2"}} will actually be translated as head {let a: HTML = meta().charset("UTF-8")
let d: HTML
if cond {
let b: HTML = title("Title 1")
d = HTMLBuilder.buildEither(first: b)
} else {
let c: HTML = title("Title 2")
d = HTMLBuilder.buildEither(second: c)
}
return HTMLBuilder.buildBlock(a, d)
}
Copy the code
The readability is greatly improved, but it is worth noting that the ViewBuilder Function currently implemented in SwiftUI using Function Builder only supports 10 generic parameters. In SwiftUI, if there are more than 10 elements of the same level, there will be strange compilation errors.
This is the official recommendation to use a combination of lowered View structures. This is different from the recommendation for Flutter. More than 10 will cause a compilation error.
The proposal, function-builders.md, is currently in draft form, so the implementation is an underlined keyword.
We can’t rule out apple tweaking the syntax a bit before the release in September, but we’re sure the feature will be ready for release.
Other new features
This article introduces some new syntax features, most of which are related to SwiftUI. WWDC Session speakers revealed the core goal of the Swift language group, Make Your Swift API Better
Mainly from the following aspects.
1, Expressive
There is no ambiguity in it
It is Easy to use
There are a number of new Swift 5.1 features not mentioned in this article, For example @dynamicCallable @dynamicMemberLookup WritableKeyPath Interested readers can refer to Swift’s complete evolution proposal swift-Evolution. (github.com/apple/swift…
Swift from 3 x Attribute
Swift has added a number of attributes (Attribut) tags since version 3.0. These attributes enhance Swift’s metaprogramming capabilities by providing information to the compiler. (docs.swift.org/swift-book/…
Swift/SwiftUI API Design Guide
Swift has been in development for 9 years (4 years of internal incubation), and the language design guidelines have been regional issues, which involve many different viewpoints from other languages. This year’s Session focuses on sorting out the following points.
Value type and reference type
When designing a data structure, choose structs or enumerations first. They are both value types, and value types have several advantages.
1. Value types are allocated on the stack, and the performance is much better than reference types, and Swift Runtime has COW optimization.
2. Value types do not have reference counts and do not raise strange multithreaded safety issues.
3. The storage attributes of value types are flat, so as to avoid the case of class inheritance in which a subclass inherits too many storage attributes and instances in memory are too large. For example, SwiftUI uses the Modifier structure optimization design.
So when do we need to use reference types? This is only necessary if the following scenarios exist.
1. You need reference counting and construction and destructor timing.
2. Data needs to be centrally managed or shared, such as singleton or data cache instance.
3. When the ID semantics and Equal semantics conflict.
The protocol is still generic
With the popularity of POP in Swift, many people involved in the API have become more accustomed to using protocols, but the official point of design is that design is evolutionary rather than one-step, and the following steps should be followed.
1. Do not declare protocols directly. 2. Start from actual business scenariosCopy the code
DynamicMemberLookup & dynamicCallable
This syntax is actually old from Swift 4.2, but in Xcode11, the IDE has been enhanced to make it easy to get code hints, and developers who have used ruby and python can easily understand that calling an instance as a method call, Static languages can be compiled and called dynamically under the premise of security, so they will be able to communicate with other languages in the future.
Abstract data access
We have introduced the new syntax PropertyWarpper in Swift 5.1. In Swift, many calculation properties are around data access, such as lazy loading, defensive replication and TLS data area, and their modes are consistent. Use PropertyWarpper to abstract common code and enhance API semantics. In SwiftUI, all core data flows use PropertyWarpper, such as @binding @state @enviromentobject @enviroment.
SwiftUI 360 ° is analyzed
In SwiftUI we no longer use the traditional Cocoa imperative layout system, but declarative layout. So what exactly is declarative layout? What is imperative layout? . For example, suppose you want to design the following layout.
In an imperative layout system, you do the following,
1. Initialize an ImageView instance
2. Set its location
3. Set its zoom level
4. Add it to the current view tree
5, if there is an event, set the event proxy or back
The things developers have to do are so many and so error-prone that it’s incredibly primitive and inefficient, but in the declarative age all you have to do is describe the information.
You have a picture on the screen, and you can place it, and you can scale it, and you can lay it out, and you can leave it to the Framework, Coder do less, Framework do more.
For declarative and imperative programming, see Wikipedia declarative programming imperative programming. In fact, declarative programming has been proposed as early as last century and has evolved for a long time. Different from imperative, declarative programming generally requires a framework to do a lot of things. In the early age of average computer performance, declarative programming was not popular, and was generally used for parsing DSL (Domain Specific Language). Such as the early HTML layout, SQL language and so on.
By the way, as early as 2006, Microsoft proposed WPF framework, which uses XAML language to write declarative UI code, while supporting event/data binding mechanism, with the universe’s first IDE Visual Studio powerful drag function, developers experience the mechanism. Unfortunately, Windows Phone hasn’t made a dent in the mobile operating system platform, so many people don’t know much about declarative UI.
Is declarative UI the future?
The React mobile platform’s Reactive Native Weex and the Flutter include the temporarily writable SwiftUI and Jetpack Compose Native platforms. Multiple data arguments and popularity show that declarative programming has unique advantages in UI layout, and I believe that UI layout will be dominated by declarative programming in the future.
The View of SwiftUI
In traditional Cocoa programming, a View can be either a UIView or an NSView that represents a visible element on the screen, depending on the platform. In SwiftUI, A View Defines A Piece of UI. It can be UIView, NSView or other implementations, depending on the platform. Views are cross-platform descriptions, and the underlying implementation is encapsulated within the framework.
SwiftUI View & Modifier
In the traditional imperative programming layout system, we usually implement some UI system structures through inheritance, and then write code to modify the appearance of the view by calling properties, such as color transparency. However, this can lead to a complex class inheritance structure, a common problem of OOP class explosion if not well designed, and by inheriting data structures, subclasses integrate the storage properties of their parent classes, resulting in large memory usage of subclass instances, even if many of the properties are not used by default. As shown in figure:
The Modifier phase of a SwiftUI winning view is abstracted as a Modifier, which usually contains only one or two storage properties. You can add a variety of modifiers to the view definition through the Extension of the original control, and their implementation at runtime is usually a closure. SwiftUI builds the real view at runtime.
SwiftUI yuan controls
In SwiftUI system we use struct to comply with View protocol and implement Body method by combining existing control description, but will the Body method recurse indefinitely?
There are six meta/main View Text Color Spacer Image Shape Divider defined in the SwiftUI system. None of them comply with the View protocol and are just basic View data structures.
Other common view components combine view structures by combining meta-controls and modifiers. Button Toggle and so on.
For SwiftUI views and modifiers, refer to Jinxiansen/SwiftUI, a quick reference compiled by Github. (github.com/Jinxiansen/…
DataFlow in SwiftUI
No program can be static, full of data states, and functions full of side effects. Traditional imperative programming manages states through member variables, which increases the complexity of states exponentially. Take the simplest example. Suppose that a view only four state, combined have 16, but the complexity of the human brain processing state is limited, the complexity of the status once more than the complexity of the human brain, can produce a lot of bugs, and take away the produced that, try to ask what students see a class full of dozens of hundreds of member attributes and global variables don’t want to strike table.
The controls in Flutter are StateLess and StateFull, and data flow is one-way. Bloc RxDart Redux is the result of many frameworks. Although the author is a fan of SwiftUI, But I have to say that many of Flutter’s ideas are very advanced. Here’s a quick analysis of how SwiftUI handles your data flow. Here we will first introduce the principle of data flow. We will focus on two points: Source of Truth refers to our real business logic data, and Dervied Value refers to the data used in the SwiftUI framework.
Constant
Usually part of the UI data is immutable, such as the Color of a Text, which we can directly use the Modifier constructor to transfer properties into it, there will be no more explanation.
Text("Hello world")
.color(.red)
Copy the code
@State
So in some cases we have a series of response events for some UI elements that cause the view to find changes. How does SwiftUI do that? With the new syntax Property warpper, all properties that use the State flag will be proxied to the State method in SwiftUI. At the same time, the framework calculates the Diff and refreshes the interface. In SwiftUI, Views area a function of State not of a sequence of events, View is the State of a real View, not a series of changing events.
struct PlayerView: View {
let episode: Episode
@State private var isPlaying = false
var body: some View {
VStack {
Text(episode.title)
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.cicle" : "play.circle")}}}}Copy the code
@Binding
Most of the time, we will always abstract to reduce the length of our code. For example, we will abstract the Button as a PlayerButton. At this time, there is a question: Should we declare the State property again?
struct PlayButton: View {
@State private var isPlaying = false
var body: some View {
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.cicle" : "play.circle")}}}Copy the code
One of the problems with this is that we generate two Derived values, and while both can tell SwiftUI to refresh the interface, synchronization between PlayerView and PlayerButton becomes a problem.
SwiftUI recommends using @binding. Let’s take a look at the implementation of Binding.
@propertyDelegate public struct Binding<Value> {
public var value: Value { get nonmutating set }
/// Initializes from functions to read and write the value.
public init(getValue: @escaping () -> Value, setValue: @escaping (Value) -> Void)
}
Copy the code
The Binding structure uses closures to capture the original attribute value, allowing the attribute to be retained by reference.
struct PlayerView: View {
let episode: Episode
@State private var isPlaying = false
var body: some View {
VStack {
Text(episode.title)
PlayButton($isPlaying)}}}Copy the code
Here State implements the BindingConvertible protocol, allowing State to be directly converted to Binding.
**@BingableObject & Combine **
In addition to being affected by user clicks, the UI sometimes receives external notifications, such as an IM message, receiving a remote message, or a timer being triggered.
SwiftUI makes it easy to respond to external changes through the new Combine framework. Developers simply implement the BindableObject protocol.
class PodcastPlayerStore: BindableObject {
var didChange = PassthoughSubject<Void, Never>()
func advance() {/ /.. didChange.send() } } struct PlayerView: View {let episode: Episode
@State private var isPlaying = false@state private var currentTime: TimeInterval = 0.0 var body: some View {VStack {Text(episode. Title) PlayButton($isPlaying)
}
.onReceive(PodcastPlayerStore.currentTimePublisher) { newTime in
self.currentTime = newTime
}
}
}
Copy the code
@ObjectBinding
State can be used to notify a single view of changes, but it can be used when you need multiple views to share the same element information and notify SwiftUI to refresh all layouts when data changes are sent
@ ObjectBinding.
final class PodcastPlayer: BindableObject {
var isPlaying: Bool = false {
didSet {
didChange.send(self)
}
}
func play() {
isPlaying = true
}
func pause() {
isPlaying = false
}
var didChange = PassthroughSubject<PodcastPlayer, Never>()
}
struct EpisodesView: View {
@ObjectBinding var player: PodcastPlayer
let episodes: [Episode]
var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "Pause": "Play")
}
)
ForEach(episodes) { episode in
Text(episode.title)
}
}
}
}
Copy the code
@propertyDelegate public struct ObjectBinding<BindableObjectType> : DynamicViewProperty where BindableObjectType : BindableObject {
@dynamicMemberLookup public struct Wrapper {
public subscript<Subject>(dynamicMember keyPath: ReferenceWritableKeyPath<BindableObjectType, Subject>) -> Binding<Subject> { get }
}
public var value: BindableObjectType
public init(initialValue: BindableObjectType)
public var delegateValue: ObjectBinding<BindableObjectType>.Wrapper { get }
public var storageValue: ObjectBinding<BindableObjectType>.Wrapper { get }
}
Copy the code
The system senses changes to external data using ObjectBinding.Wrapper.
@EnviromemntObject
Sometimes some environment variables are shared, and shared information can be retrieved via EnviromentObject, which is passed back down the View tree structure. Similar to the Theme and ScopeModel of Flutter, they are relatively simple and will not be explained here.
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIHostingController(rootView: LandmarkList().environmentObject(UserData()))
struct LandmarkList: View {
@EnvironmentObject private var userData: UserData
var body: some View {
Text("")}Copy the code
@Enviroment
The environment information mentioned above refers to user-defined shared information. However, the system has a large number of built-in environment information, such as time zone and color mode. The SwiftUI can directly subscribe to the system environment information, so that the SwiftUI can automatically obtain the environment information changes and refresh the layout.
struct CalendarView: View {
@Environment(\.calendar) var calendar: Calendar
@Environment(\.locale) var locale: Locale
@Environment(\.colorScheme) var colorScheme: ColorScheme
var body: some View {
return Text(locale.identifier)
}
}
Copy the code
conclusion
SwiftUI’s data flow management solutions mentioned in various tutorials and WWDC sessions are summarized as follows:
1. Transfer constant to SwiftUI directly.
2. Use @state to manage the State on the control.
3. Use BindableObject to send notifications for external event changes.
4. Use @ObjectBinding to manage view mutable data that needs to be shared.
5. Don’t synchronize multiple states. Use @binding to share one Source of Truth.
6. Use @enviroment for system environment management.
7. Use @enviromemntobject to manage immutable data that needs to be shared.
@binding has reference semantics, and can work well with @binding @objectBinding @state to avoid multiple data unsynchronization.
SwiftUI layout algorithm
I believe that many people are curious about the internal layout algorithm of SwiftUI after reading SwiftUI. In Session 237 Building Custom Views with SwiftUI, a brief introduction is given. SwiftUI will obtain the control information describing the view through the return value of the body and convert it into the corresponding internal view information, which is handed to 2D drawing engine Metal or Open GL for drawing. Among them, the complex Toggle may be referenced from the original UIKit implementation.
Content View
For the following code:
import SwiftUI
struct ContentView : View {
var body: some View {
Text("Hello World!")}}Copy the code
Their structure is RootView -> ContentView -> Text. How does Text appear on the screen? The official description is the following three steps
The child calculates its own size. 3. The parent view places the child view in its own coordinate system according to the size of the child viewCopy the code
The important second step is that there are usually three ways to set the size for a view description.
1. No calculation is required. According to the content, for example, Image is as large as the picture, and Text is the calculated visual range, similar to NSString, which calculates the width and height according to the font.
2. Frame Specifies the width and height
3. Set the zoom ratio. For example, set aspectRatio for Image.
By the way, Apple mentioned that the calculated fuzzy coordinate points in SwiftUI will be aligned to clear pixels to avoid the jagged feeling.
So for the use of the Modifier layout structure is as follows:
import SwiftUI
struct ContentView : View {
var body: some View {
Text("Hello World!")
.padding(10)
.background(Color.Green)
}
}
struct Avocado : View {
var body: some View {
Image("20x20_avocado")
.frame(width: 30, height: 30)
}
}
Copy the code
It looks like the Frame and the Background and the Padding and all the other elements are present in the View structure, but they’re really constraints on the View, not the View itself.
HStack/VStack
HStack and ZStack are very similar to Android LinerLayout, and the algorithm is similar to Flex layout. For the layout below, Apple adds space between the controls that conforms to the Apple Guidelines for human Interaction to ensure elegance and consistency in the UI.
HStack {
VStack {
Text(Painted painted painted painted "u")
Text("5 stars")
}
.font(.caption)
VStack {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
.font(.caption)
.lineLimit(1)
}
}
Copy the code
How is Stack computed as above? Set the Stack spindle length to W1.
2. Obtain the remaining spindle width W2= W1-n * S. 3. Allocate an estimated width evenly. 5. Calculate from front to back along the main axis. If the calculated width is less than the estimated width, it will be displayed normally. The default cross axis alignment is Center, and the Stack occupies the boundary containing the largest element.Copy the code
The default calculation is to compute the layout sequentially, but if certain elements are important, you can use the LayoutPriority Modifier to increase the LayoutPriority and avoid view truncation.
Cross axis alignment
A lot of times when we’re developing layout systems we’re going to nest a lot of HStacks and zStacks and sometimes we’re going to align them with custom alignment scales in addition to internal alignment.
extension VerticalAlignment {
public static let top: VerticalAlignment
public static let center: VerticalAlignment
public static let bottom: VerticalAlignment
public static let firstTextBaseline: VerticalAlignment
public static let lastTextBaseline: VerticalAlignment
}
extension HorizontalAlignment {
public static let leading: HorizontalAlignment
public static let center: HorizontalAlignment
public static let trailing: HorizontalAlignment
}
extension VerticalAlignment {
private enum MidStarAndTitle: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> Length {
return d[.bottom]
}
}
static letMidStarAndTitle = VerticalAlignment(midStarAndTitle.self)} .midStarAndTitle) { VStack { Text(Painted painted painted painted "u")
Text("5 stars")
}
.font(.caption)
VStack {
HStack {
Text("Avocado Toast").font(.title)
Spacer()
Image("20x20_avocado")
}
Text("Ingredients: Avocado, Almond Butter, Bread, Red Pepper Flakes")
.font(.caption)
.lineLimit(1)
}
}
Copy the code
ZStack
In addition to HStack and VStack, there’s ZStack, and ZStack is laid out like absolute positioning, like adding different subviews to a View in UIKit.
VStack {
Text("Hello")
Text("World").padding(10);
}
Copy the code
The VStack aligns in both horizontal and vertical directions.
public struct Alignment : Equatable {
public var horizontal: HorizontalAlignment
public var vertical: VerticalAlignment
@inlinable public init(horizontal: HorizontalAlignment, vertical: VerticalAlignment)
public static let center: Alignment
public static let leading: Alignment
public static let trailing: Alignment
public static let top: Alignment
public static let bottom: Alignment
public static let topLeading: Alignment
public static let topTrailing: Alignment
public static let bottomLeading: Alignment
public static let bottomTrailing: Alignment
public static func == (a: Alignment, b: Alignment) -> Bool
}
Copy the code
Shape in SwiftUI
Drawing in SwiftUI is similar to using UIBezierPath API before, only need to implement Shape description can not be described here.
struct WedgeShape: Shape {
var wedge: Ring.Wedge
func path(in rect: CGRect) -> Path {
var p = Path()
let g = WedgeGeometry(wedge, in: rect)
p.addArc(center: g.cen, radius: g.r0, startAngle: g.a0, endAngle: g.a1, clockwise: false)
p.addLine(to: g.topRight)
p.addArc(center: g.cen, radius: g.r1, startAngle: g.a1, endAngle: g.a0, clockwise: true)
p.closeSubpath()
return p
}
}
Copy the code
SwiftUI mixed layout
Many existing apps largely use Cocoa’s traditional imperative UI layout. Even if they do not directly introduce SwiftUI with iOS 13.x, it is impossible to transform them into SwiftUI altogether, and there is bound to be mixed programming for a long time. Apple has taken this into account and provides a very easy way to mix programming.
UIKit is embedded in SwiftUI View
This is the most common scenario SwiftUI provided UI/NS/WKHostingController packing a SwiftUI View, and provides for the View update time setNeedsBodyUpdate updateBodyIfNeeded
SwiftUI View embedded in UIKit View
Developers have packaged a number of available view components that can be embedded directly into SwiftUI for seamless development.
I’m not going to explain this too much, but the code and timing are very clear. Demo can refer to the Session 231 integrating swiftui (developer.apple.com/videos/play…
SwiftUI On All Device
As mentioned in the previous article “SwiftUI — Taobao for your first experience”, Apple is not considering the cross-platform of RN, Flutter Weex, but the cross-platform of TV, Watch, Mac, iPad. Therefore, the concept is not consistent. Apple specifically points out that for different devices, There is no one-size-fits-all approach to all devices. If there is a risk that many platform-specific experiences will be erased, which is developer-friendly but not necessarily a good thing for users, Apple has pointed to a design philosophy for multiple devices.
2. Carefully analyze the design of shared module. 3. Sharing view is a risky behavior, which should be considered further.Copy the code
The view layer described by the developers in SwiftUI is just the definition and description of the data. Enough is done within the SwiftUI framework, so learning SwiftUI can easily start development on other platforms. For more details please refer to the Session 240 SwiftUI on all Device and 219 SwiftUI Session on watchOS (developer.apple.com/videos/play…
accessibility in SwiftUI
This year, SwiftUI has also taken accessibility into consideration. Apple is a very humane company, and SwiftUI has reduced the workload of accessibility development. Session 238 accessibility in Swiftui (developer.apple.com/videos/play…
Reference for this article:
1, SE - 0255 omit - return (https://github.com/apple/Swift-evolution/blob/master/proposals/0255-omit-return.md) 2, SE - 0242 default-values-memberwise (https://github.com/apple/Swift-evolution/blob/master/proposals/0242-default-values-memberwise.md) 3, SE - 0228 expressiblebystringinterpolation (https://github.com/apple/Swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md) 4, SE - 0258 Property - wrappers (https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md) 5,function-builders.md (https://github.com/apple/swift-evolution/blob/9992cf3c11c2d5e0ea20bee98657d93902d5b174/proposals/XXXX-function-builders Md) 6, swift - evolution (https://github.com/apple/swift-evolution/tree/master/proposals) 7, the Attribute of (https://docs.swift.org/swift-book/ReferenceManual/Attributes.html) 8, Inside SwiftUI's Declarative Syntax'9, Session402 s Compiler Magic (https://swiftrocks.com/inside-swiftui-compiler-magic.html) (https://developer.apple.com/videos/play/wwdc2019/402/) 10, API Design GuideLine (https://swift.org/documentation/api-design-guidelines/) 11, new web celebrity SwiftUI - 12, Session415 taobao take you early experience (https://developer.apple.com/videos/play/wwdc2019/415) 13, declarative programming 14, imperative programming (https://zh.wikipedia.org/wiki/%E5%AE%A3%E5%91%8A%E5%BC%8F%E7%B7%A8%E7%A8%8B) (https://zh.m.wikipedia.org/zh-my/%E6%8C%87%E4%BB%A4%E5%BC%8F%E7%B7%A8%E7%A8%8B) 15 and understanding - property - wrappers - in - swiftui (https://mecid.github.io/2019/06/12/understanding-property-wrappers-in-swiftui/), 16, 402 What Session's New Swift (https://developer.apple.com/videos/play/wwdc2019/402/) in 17, Session 204 Introducing SwiftUI: Building Your First App (https://developer.apple.com/videos/play/wwdc2019/204/), 18, 216 Introducing Session SwiftUI Essentials (https://developer.apple.com/videos/play/wwdc2019/216/) 19, the Session 226 SwiftUI Essentials (https://developer.apple.com/videos/play/wwdc2019/226/), 20, 231 Session Integrating SwiftUI (https://developer.apple.com/videos/play/wwdc2019/226/), the Session 21 237 Building Custom Views with SwiftUI (https://developer.apple.com/videos/play/wwdc2019/231/), 22, 238 the org.eclipse.swt.accessibility Session in swiftui (https://developer.apple.com/videos/play/wwdc2019/238/), 23, 240 SwiftUI Session on all Device 24, 219 SwiftUI Session on watchOS (https://developer.apple.com/videos/play/wwdc2019/240/) (https://developer.apple.com/videos/play/wwdc2019/219/), 25, 415 Modern Swift API Design Session (https://developer.apple.com/videos/play/wwdc2019/415/), 26, 30 minutes to learn Flex layout (https://zhuanlan.zhihu.com/p/25303493)Copy the code