A UI can be defined as a set of views that display data, react to events, and are located in a certain way on the screen.
How do we place items on the screen?
We need to use code for special cases.
The described tools are sharpened for typical cases, often we cannot (or can, but it is very inconvenient) to describe the location of an element with the help of these tools. You have to do it in code. The logic of the layout description is spread over several places.
There must be a better way.
Layout is a function
What is layout? This is a description of the location of the elements. It can be expressed as a function.
The input function receives the size of the container and an array of elements, the output gives the coordinates of these elements.
Theoretically, the layout can be any - here as a designer draws. Therefore, to describe such a function, we need a turing-complete language. UI frameworks offer us anything, just not turing-full language, hence the problems. It would be logical to take the language in which the rest of the program is written: for iOS, objc / swift, for android, java / kotlin, etc.
We can easily describe the layout in code
We can easily describe layout in code! It is enough to write one function. Often we will have elements relative to the existing ones (to the right, below, etc.). You can hide such calculations in a function with readable names. Under iOS, this is done in the Facade (objc) , Neon (swift) libraries. You can easily write your own in any language.
In iOS, if you want to define a custom layout for a view, you need to override the layoutSubviews()
method (or the viewDidLayoutSubviews()
) and set the frame to its children. This method is called when changing the size of the view, that is, turns, Split View and so on. This method will be generated.
Here is an example from the official Neon page:
let isLandscape : Bool = UIDevice.currentDevice().orientation.isLandscape.boolValue let bannerHeight : CGFloat = view.height() * 0.43 let avatarHeightMultipler : CGFloat = isLandscape ? 0.75 : 0.43 let avatarSize = bannerHeight * avatarHeightMultipler searchBar.fillSuperview() bannerImageView.anchorAndFillEdge(.Top, xPad: 0, yPad: 0, otherSize: bannerHeight) bannerMaskView.fillSuperview() avatarImageView.anchorInCorner(.BottomLeft, xPad: 15, yPad: 15, width: avatarSize, height: avatarSize) nameLabel.alignAndFillWidth(align: .ToTheRightCentered, relativeTo: avatarImageView, padding: 15, height: 120) cameraButton.anchorInCorner(.BottomRight, xPad: 10, yPad: 7, width: 28, height: 28) buttonContainerView.alignAndFillWidth(align: .UnderCentered, relativeTo: bannerImageView, padding: 0, height: 62) buttonContainerView.groupAndFill(group: .Horizontal, views: [postButton, updateInfoButton, activityLogButton, moreButton], padding: 10) buttonContainerView2.alignAndFillWidth(align: .UnderCentered, relativeTo: buttonContainerView, padding: 0, height: 128) buttonContainerView2.groupAndFill(group: .Horizontal, views: [aboutView, photosView, friendsView], padding: 10)
Not bad for 10 lines of code, right?
Often, the UI is declared declaratively in the form of markup (xml, xaml, xib), which then turns into code (Qt, Delphi, C # WinForms) or from which a ready-made object graph (xib, xaml) is created. In this case, IDE provide a means of preview, which is very convenient.
Describing the layout in code, we discard the preview tools.
Usually the layout goes from top to bottom: we have the screen size, knowing this size we will lay out the first layer of the views, getting their dimensions go deeper and so on. Sometimes you need to go the other way: stretch the container to the size of the contents. Describing this is not always convenient.
In my opinion, this is a good approach if you are already writing UI in code. The code is about the same as when using Autolayout, but it works faster and more predictable.
The main disadvantage is that visibility in the form of preview tools is lost.
Source: https://habr.com/ru/post/343318/
All Articles