1import Layout, { Code } from '../../components/docs.js';
2export default Layout;
3
4# Installation
5Start by creating a new project using Xcode and CocoaPods and navigate to your project's `Podfile`. Here we add the stretch dependency and regenerate our workspace using `pod install`. If you prefer not using CocoaPods then you can go ahead and grab the Xcode project in the Stretch repo and add that manually as a dependency.
6
7<Code lang="ruby" file="Podfile">{`
8platform :ios, '10.0'
9use_frameworks!
10
11target 'MyApp' do
12 pod 'StretchKit', '~> 0.2.2'
13end
14`}</Code>
15
16We are ready to use Stretch! Open up `ViewController.swift` and replace its contents with the following. Once finished you should be able to run the project to see the results of this basic layout calculation in the logs.
17
18<Code lang="swift" file="ViewController.swift">{`
19import UIKit
20import StretchKit
21
22class ViewController: UIViewController {
23
24 override func viewDidLoad() {
25 let node = Node(
26 style: Style(size: Size(width: .points(100.0), height: .points(100.0))),
27 children: []
28 )
29
30 let layout = node.computeLayout(thatFits: Size(width: nil, height: nil))
31 print("width: \(layout.width), height: \(layout.height)")
32 }
33}
34`}</Code>
35
36# Basics
37`Node`s are the core building blocks of a stretch layout tree. A node needs a `Style` which describes the `flexbox` properties for the node. By adding nodes as children to a parent node we create a tree which we can perform layout on. The result of a layout computation is a tree of `Layout` nodes. This tree matches the structure of the `Node` tree but contains the computed layout of each node.
38
39<Code lang="ViewController" file="ViewController.swift">{`
40import UIKit
41import StretchKit
42
43class ViewController: UIViewController {
44
45 override func viewDidLoad() {
46 let node = Node(
47 style: Style(),
48 children: [
49 Node(style: Style(size: Size(width: .percent(0.5), height: .percent(0.5))), children: [])
50 ]
51 )
52
53 let layout = node.computeLayout(thatFits: Size(width: nil, height: nil))
54 layout.width; // 100.0
55 layout.height; // 100.0
56 layout.children.size(); // 1
57 }
58}
59`}</Code>
60
61Nodes can be be mutated and stretch will automatically recompute only the subtrees which have changed. This is incredibly powerfull if you have a large node tree, perhaps representing the UI of an app, and only need to change the height of a single element.
62
63<Code lang="swift" file="ViewController.swift">{`
64import UIKit
65import StretchKit
66
67class ViewController: UIViewController {
68
69 override func viewDidLoad() {
70 let node = Node(
71 style: Style(),
72 children: [
73 Node(style: Style(size: Size(width: .percent(0.5), height: .percent(0.5))), children: [])
74 ]
75 )
76
77 node.computeLayout(thatFits: Size(width: nil, height: nil))
78
79 // Mutate node
80 node.setStyle(Style(size: Size(width: .percent(0.5), height: .percent(0.5))))
81
82 // This call will return partially cached results
83 node.computeLayout(thatFits: Size(width: nil, height: nil))
84 }
85}
86`}</Code>
87
88# Node
89Create a new node with children (or an empty list). Children can be removed or added later on as well.
90
91<Code lang="swift">{`init(style: Style, children: Array<Node>)`}</Code>
92
93----
94
95Create a new leaf node. This is a node without children which knows how to measure its own intrinsic size. Examples of common leaf nodes are images and text.
96
97<Code lang="swift">{`init(style: Style, measure: @escaping MeasureFunc)`}</Code>
98
99----
100
101Set/Get the measure function on a node. This function will be called on leaf nodes to resolve the instrinsic size of the node given a set of constraints.
102
103<Code lang="swift">{`var measure: MeasureFunc?`}</Code>
104
105----
106
107Adds a child to an existing node.
108
109<Code lang="swift">{`func addChild(_ child: Node)`}</Code>
110
111----
112
113Set/Get the children of an existing node.
114
115<Code lang="swift">{`var children: Array<Node>`}</Code>
116
117----
118
119Removes a previously added child from an existing node.
120
121<Code lang="swift">{`func removeChild(_ child: Node) -> Node`}</Code>
122
123----
124
125Removes the child at the given index of an existing node.
126
127<Code lang="swift">{`func removeChild(at index: Int) -> Node`}</Code>
128
129----
130
131Replaces the child at the given index of an existing node with the given new child.
132
133<Code lang="swift">{`func replaceChild(_ child: Node, at index: Int) -> Node`}</Code>
134
135----
136
137Set/Get the style of an existing node.
138
139<Code lang="swift">{`var style: Style`}</Code>
140
141----
142
143Marks the node as dirty and propagates this up the node tree. This function is called internally for any mutating function. The only time you have to ensure to call it manually is if some application state has changed which will effect the intrinsic size of a leaf node. For example if the text of a text node has changed.
144
145<Code lang="swift">{`func markDirty()`}</Code>
146
147----
148
149Returns wether of not this node is marked as dirty and will need to be recalculated during the next layout pass.
150
151<Code lang="swift">{`var dirty: Bool`}</Code>
152
153----
154
155Computes the layout of a node tree given a size. The size passed in is used to compute percentage sizes on the root node. If you don't need to do this then it is fine to pass `Size(width: nil, height: nil)` however you will typically pass in the size of the viewport which will host this computed layout. The resulting tree of `Layout` nodes mimics the structure of the computed `Node` tree and includes position and size information..
156
157<Code lang="swift">{`func computeLayout(thatFits size: Size<Float?>) -> Layout`}</Code>
158
159# Style
160The `Style` object contains all the properties associated with flexbox as well as some properties which we found useful outside of flexbox. For example `positionType` can be set to `PositionType.absolute` which puts the node into a absolute layout context instead of a flexbox context. `aspectRatio` is another property not part of the flexbox specification which when set ensures the node matches a certain aspect ratio.
161
162<Code lang="swift">{`
163class Style {
164 let display: Display
165 let positionType: PositionType
166 let direction: Direction
167 let flexDirection: FlexDirection
168 let flexWrap: FlexWrap
169 let overflow: Overflow
170 let alignItems: AlignItems
171 let alignSelf: AlignSelf
172 let alignContent: AlignContent
173 let justifyContent: JustifyContent
174 let position: Rect<Dimension>
175 let margin: Rect<Dimension>
176 let padding: Rect<Dimension>
177 let border: Rect<Dimension>
178 let flexGrow: Float
179 let flexShrink: Float
180 let flexBasis: Dimension
181 let size: Size<Dimension>
182 let minSize: Size<Dimension>
183 let maxSize: Size<Dimension>
184 let aspectRatio: Float?
185}
186`}</Code>
187
188----
189
190`AlignItems` describes how to align children along the cross axis of their parent. `AlignItems` is very similar to `JustifyContent` but instead of applying to the main axis, `AlignItems` applies to the cross axis.
191
192- `flexStart` aligns children of a container to the start of the container's cross axis.
193- `flexEnd` aligns children of a container to the end of the container's cross axis.
194- `center` aligns children of a container in the center of the container's cross axis.
195- `baseline` aligns children of a container to the baseline of that container's cross axis.
196- `stretch` stretches children of a container to match the height of the container's cross axis.
197
198Optional, if left undefined `stretch` is the default value.
199
200<Code lang="swift">{`
201enum AlignItems: Int {
202 vase flexStart
203 case flexEnd
204 case center
205 case baseline
206 case stretch
207}
208`}</Code>
209
210----
211
212`AlignSelf` has the same options and effect as setting `AlignItems` on the containing node but instead of affecting the children within the node you can apply this property to a single child to change its alignment within its parent. `AlignSelf` overrides any option set on the parent with `AlignItems`.
213
214- `auto` delegates to the parent's `AlignItems` property.
215- `flexStart` aligns children of a container to the start of the container's cross axis.
216- `flexEnd` aligns children of a container to the end of the container's cross axis.
217- `center` aligns children of a container in the center of the container's cross axis.
218- `baseline` aligns children of a container to the baseline of that container's cross axis.
219- `stretch` stretches children of a container to match the height of the container's cross axis.
220
221Optional, if left undefined `auto` is the default value.
222
223<Code lang="swift">{`
224enum AlignSelf: Int {
225 case auto
226 case flexStart
227 case flexEnd
228 case center
229 case baseline
230 case stretch
231}
232`}</Code>
233
234----
235
236`AlignContent` defines the distribution of lines along the cross-axis. This only has effect when items are wrapped to multiple lines using `FlexWrap`.
237
238- `flexStart` aligns wrapped lines to the start of the container's cross axis.
239- `flexEnd` aligns wrapped lines to the end of the container's cross axis.
240- `center` aligns wrapped lines in the center of the container's cross axis.
241- `stretch` stretches wrapped lines to match the height of the container's cross axis.
242- `spaceBetween` evenly spaces wrapped lines across the container's main axis, distributing remaining space between the lines.
243- `spaceAround` evenly spaces wrapped lines across the container's main axis, distributing remaining space around the lines. Compared to space between using space around will result in space being distributed to the begining of the first lines and end of the last line.
244
245Optional, if left undefined `stretch` is the default value.
246
247<Code lang="swift">{`
248enum AlignContent: Int {
249 case flexStart
250 case flexEnd
251 case center
252 case stretch
253 case spaceBetween
254 case spaceAround
255}
256`}</Code>
257
258----
259
260The `Direction` property specifies wether the component should be laid out in `ltr` or `rtl` writing mode.
261
262Optional, `inherit` by default (root node will implicitly default to `ltr`).
263
264<Code lang="swift">{`
265enum Direction: Int {
266 case inherit
267 case ltr
268 case rtl
269}
270`}</Code>
271
272----
273
274The `Display` property specifies wether the component should be laid out as `flex` or not at all. If `none` is specified then the node will have a resulting size of zero. In the future this enum will be expanded to include more display options such as `grid`.
275
276Optional, `flex` by default.
277
278<Code lang="swift">{`
279enum Display: Int {
280 case flex
281 case none
282}
283`}</Code>
284
285----
286
287`FlexDirection` controls the direction in which children of a node are laid out. This is also referred to as the main axis. The main axis is the direction in which children are laid out. The cross axis the the axis perpendicular to the main axis, or the axis which wrapping lines are laid out in.
288
289- `row` aligns children from left to right. If wrapping is enabled then the next line will start under the first item on the left of the container.
290- `column` aligns children from top to bottom. If wrapping is enabled then the next line will start to the left first item on the top of the container.
291- `rowReverse` aligns children from right to left. If wrapping is enabled then the next line will start under the first item on the right of the container.
292- `columnReverse` aligns children from bottom to top. If wrapping is enabled then the next line will start to the left first item on the bottom of the container.
293
294Optional, if left undefined `row` is the default value.
295
296<Code lang="swift">{`
297enum FlexDirection: Int {
298 case row
299 case column
300 case rowReverse
301 case columnReverse
302}
303`}</Code>
304
305----
306
307`JustifyContent` describes how to align children within the main axis of their container. For example, you can use this property to center a child horizontally within a container with `FlexDirection` set to `FlexDirection.row` or vertically within a container with `FlexDirection` set to `FlexDirection.column`.
308
309- `flexStart` aligns children of a container to the start of the container's main axis.
310- `flexEnd` aligns children of a container to the end of the container's main axis.
311- `center` aligns children of a container in the center of the container's main axis.
312- `spaceBetween` evenly spaces of children across the container's main axis, distributing remaining space between the children.
313- `spaceAround` evenly spaces of children across the container's main axis, distributing remaining space around the children. Compared to space between using `spaceAround` will result in space being distributed to the beginning of the first child and end of the last child.
314- `spaceEvenly` evenly spaces of children across the container's main axis, distributing remaining space around the children so that visually there is equal space around every child including at the edges.
315
316Optional, if left undefined `flexStart` is the default value.
317
318<Code lang="swift">{`
319enum JustifyContent: Int {
320 case flexStart
321 case flexEnd
322 case center
323 case spaceBetween
324 case spaceAround
325 case spaceEvenly
326}
327`}</Code>
328
329----
330
331The `Overflow` property specifies how the node should handle children which overflow their parent's size. This property is currently not used but may be used in the future.
332
333Optional, `visible` by default.
334
335<Code lang="swift">{`
336enum Overflow: Int {
337 case visible
338 case hidden
339 case scroll
340}
341`}</Code>
342
343----
344
345The `PositionType` property specifies wether the component should be laid out relative to its sibling or absolutely positioned within its parent.
346
347Optional, `relative` by default.
348
349<Code lang="swift">{`
350enum PositionType: Int {
351 case relative
352 case absolute
353}
354`}</Code>
355
356----
357
358The `FlexWrap` property controls what happens when children overflow the size of the container along the main axis. By default children are forced into a single line (which can shrink components).
359
360- `wrap` enables wrapping of children if they overflow the main axis of the container.
361- `noWrap` disables wrapping of children, children will instead shrink to fit the main axis or overflow depending on their `flexShrink` setting.
362- `wrapReverse` enables wrapping of children if they overflow the main axis of the container. Unlike `wrap`, lines are laid out in reverse order.
363
364Optional, if left undefined `noWrap` is the default value.
365
366<Code lang="swift">{`
367enum FlexWrap: Int {
368 noWrap
369 wrap
370 wrapReverse
371}
372`}</Code>
373
374----
375
376When `PositionType` is set to `PositionType.relative` the `position` property specifies the offset the node should have from its default position. In the case position is set to `PositionType.absolute` this property indicates the distance this node's edge should be from the container's corresponding edge.
377
378All position properties are optional and `Dimension.undefined` by default.
379
380<Code lang="swift">{`position: Rect<Dimension>`}</Code>
381
382----
383
384Margin effects the spacing around the outside of the node it is applied to. A node with margin will offset itself from the bounds of its parent but also offset the location of any siblings. If margin is set to auto then the margin is equal to the remaining space in that axis divided between all other auto margin values in that axis.
385
386As apposed to layout engines Stretch does not support setting left/right margin but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corrosponds to left and end to right, the opposite is true in right-to-left locales.
387
388All margin properties are optional and `Dimension.undefined` by default, resulting in no margin.
389
390<Code lang="swift">{`margin: Rect<Dimension>`}</Code>
391
392----
393
394Padding affects the size of the node it is applied to. Padding will not add to the total size of a node if it has an explicit size set, instead it will inset the content. For auto-sized nodes padding will increase the size of the node as well as offset the location of any children.
395
396As apposed to layout engines Stretch does not support setting left/right padding but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corrosponds to left and end to right, the opposite is true in right-to-left locales.
397
398All padding properties are optional and `Dimension.undefined` by default, resulting in no padding.
399
400<Code lang="swift">{`padding: Rect<Dimension>`}</Code>
401
402----
403
404Border affects the size of the node it is applied to. Border will not add to the total size of a node if it has an explicit size set, instead it will inset the content. For auto-sized nodes border will increase the size of the node as well as offset the location of any children.
405
406As apposed to layout engines Stretch does not support setting left/right border but instead uses start/end to ensure equal support for right-to-left as well as left-to-right locales. In left-to-right locales start corrosponds to left and end to right, the opposite is true in right-to-left locales.
407
408All border properties are optional and `Dimension.undefined` by default, resulting in no border.
409
410<Code lang="swift">{`border: Rect<Dimension>`}</Code>
411
412----
413
414Flex grow describes how any space within a container should be distributed among its children along the main axis. After laying out its children, a container will distribute any remaining space according to the flex grow values specified by its children. Flex grow accepts any floating point value >= 0. A container will distribute any remaining space among its children weighted by the child’s flex grow value. If `flexGrow` is set to 0 the node will not grow.
415
416Optional, if left undefined `0.0` is the default value.
417s
418<Code lang="swift">{`flexGrow: Float`}</Code>
419
420----
421
422Flex shrink describes how to shrink children along the main axis in the case that the total size of the children overflow the size of the container on the main axis. flex shrink is very similar to flex grow and can be thought of in the same way if any overflowing size is considered to be negative remaining space. These two properties also work well together by allowing children to grow and shrink as needed. Flex shrink accepts any floating point value >= 0. A container will shrink its children weighted by the child’s flex shrink value. If `flexShrink` is set to 0 the node will not shrink.
423
424Optional, if left undefined `1.0` is the default value.
425
426<Code lang="swift">{`flexShrink: Float`}</Code>
427
428----
429
430Flex basis is an axis-independent way of providing the default size of an item along the main axis. Setting the flex basis of a child is similar to setting the width of that child if its parent is a container with `FlexDirection.row` or setting the height of a child if its parent is a container with `FlexDirection.column`. The flex basis of an item is the default size of that item, the size of the item before any flex grow and flex shrink calculations are performed.
431
432Optional, similar to width and height the default value is undefined which means that the flex basis is determined by the size of the node's content.
433
434<Code lang="swift">{`flexBasis: Dimension`}</Code>
435
436----
437
438The size property specifies the size of the node’s content area. Size can be specified in pixels, points, or a percentage of the parent size. If set to `Dimension.undefined` or `Dimension.auto` the size is calculated based on the node’s content. What is meant by content is based on the kind of node, for example the size of a text node is the size of the text being displayed while the size of a flexbox node is the total size of its children.
439
440Optional, `Size(Dimension.auto, Dimension.auto)` by default.
441
442<Code lang="swift">{`size: Size<Dimension>`}</Code>
443
444----
445
446The maximum size of a node, this property takes priority over all other properties. If left undefined the node has no maximum size.
447
448Optional, `Size(Dimension.auto, Dimension.auto)` by default.
449
450<Code lang="swift">{`minSize: Size<Dimension>`}</Code>
451
452----
453
454The minimum size of a node, this property takes priority over all other properties. If left undefined the node has no minimum size.
455
456Optional, `Size(Dimension.auto, Dimension.auto)` by default.
457
458<Code lang="swift">{`maxSize: Size<Dimension>`}</Code>
459
460----
461
462Configures the aspect ratio of a node, very useful for media content.
463
464- Accepts any floating point value > 0.
465- Defined as the ratio between the width and the height of a node e.g. if a node has an aspect ratio of 2 then its width is twice the size of its height.
466- Respects the min and max dimensions of an item.
467- Has higher priority than flex grow
468- If aspect ratio, width, and height are set then the cross axis dimension is overridden.
469
470Optional, if left undefined no aspect ratio is enforced.
471
472<Code lang="swift">{`aspectRatio: Float?`}</Code>
473
474# Layout
475`Layout` nodes are returned from a layout calculation and mimic the structure of the computed `Node` tree.
476
477<Code lang="swift">{`
478class Layout {
479 let x: Float
480 let y: Float
481 let width: Float
482 let height: Float
483 let children: Array<Layout>
484}
485`}</Code>