Layout for Apple Watch is based on placing objects to storyboard. Every new object is placed just under the previous one etc. There is also possible to use groups and align objects same way horizontally. (see details here) I would appreciate to have the same functionality on iOS - to place UIViews this way and define them same attributes like alignment, width relative to container etc. just like in WatchKit.
It is also similar with LinearLayout on Android. I found CSLinearLayoutView on github, but its very old and it doesn't use autolayout.
Does any good library for this purpose exist?
If not.. It looks very easy to implement this by myself, do you see any key problems or limitations which would make it impossible?
I don't think there is or will be any addon for the Interface Builder itself, so you could use the same (ugly watchkit) constraints for iOS apps. AutoLayout for iOS is way better and more flexible to use than that WatchKit mess inside IB (Interface Builder of Xcode).
I do prefer to do most of the design work in my code so I don't have to worry if the IB will create some unnecessary stuff for me. If you want some drag and drop, you should use use IB and learn how to use AutoLayout.
The interesting part:
If you want to create your design in code and want some library which is easy to use, you can try on of these: Masonry (Obj-C) or SnapKit (Same Lib but in Swift). Both libs are very strong and up to date. I don't know if the dev implemented a custom alignment to center a subview in its superview. You should check that by yourself. :) If you like the VFL (Visual Format Language), like I do. You should search for a similar Lib or wait until I'm done with my project some day. :D I was inspired by SnapKits shiny syntax and started creating my own framework for VFL constraints. I also implemented the possibility to center my subview in its superview.
let spacer1 = View(name: "spacer1")
let spacer2 = View(name: "spacer2")
let view1 = AutoLayoutView(name: "view1")
view1.backgroundColor = UIColor.whiteColor()
view1.layer.cornerRadius = 50
let view2 = AutoLayoutView(name: "view2")
view2.backgroundColor = UIColor.redColor()
view2.layer.cornerRadius = 10
self.rootView.addSubview(spacer1)
self.rootView.addSubview(spacer2)
self.rootView.addSubview(view1)
self.rootView.addSubview(view2)
self.rootView.addConstraints { (add) -> Void in
add.group({ (add) -> Void in
add.normal.vertical.format("[view1(100)]").alignCenterHorizontaly
add.normal.vertical.format("[view2(30)]")
add.normal.horizontal.format("[view1(100)][spacer1]").alignCenterVerticaly
add.normal.horizontal.format("[spacer1][view2(40)]").alignCenterY
add.normal.horizontal.format("[view2][spacer2(==spacer1)]|")
}, identifier: "GroupName")
}
And with this we we get something like that:
There is still a lot work to do, so I can't public unfinished work yet.
I hope I could help you a little. :)
Introduced in iOS 9 as UIStackView - https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/
Custom UIStackView implementation for iOS 7+
https://github.com/oarrabi/OAStackView
Related
Currently I am working on a project which got all the task done in a singe viewController. As there are so many elements on the viewController I choose to do the UI with coding, like this:
let myButton: UIButton = {
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(openFunction), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
As there are so many button, view, texifield, label etc along with all their constraints written with code, my viewController class getting bigger and bigger. How can I keep all the UI code in separate file and integrate in on my viewController ? I don't know there might be really easy way to do that but I am actually struggling. Any suggestion would be really helpful.
Welcome to the world of design patterns and code architecture. They are various ways to accomplish what you are after. It's a good sign you are able to identify this problem early.
You can start looks at MVVM, VIPER, ReSwift among others. Research which fits your the requirements of your app.
Suggestions for Reducing UI Code in view controller:
In terms of reducing just the UI Code growing in the view controller, I suggest start creating subclasses of common elements and keey your code DRY. For instance, if a UIButton with same fonts and borders etc are being created many times then look at creating a subclass for it and move the configurations inside this subclass.
You can also create subview of logical elements on the screen, example you have a header with buttons and labels then move it into a subclass and start using this subclass from here on. This should improve your code readability and reuse.
You can also reduce a lot of the autlayout code by create extensions of commons layouts like pinning to all corners etc this way the repetition of boilerplate auto layout code is much less.
An alternative to what carbonr has proposed is to leverage Interface Builder. With Interface Builder, you can create one or more StoryBoards and separate UI elements and constraints from the controller that contains your code. Obviously, if you are unfamiliar with Interface Builder there would be a learning curve.
A specific answer to your specific code would be to create a convenience initializer in an extension to UIButton.
extension UIButton {
convenience init(_ target:Any, _ action:Selector) {
self.init(CGRect.zero)
self.addTarget(target, action:action)
self.translatesAutoresizingMaskIntoConstraints = false
}
}
Right there you are probably cutting back on things in your VC.
Next, consider moving this - and all - UI code out of your VC file and into other files. I typically move my extensions/subclasses into as many files as needed. (The build may take longer but the final binary should be the same size.) For large projects, this helps make things manageable.
At the same time consider making an extension to your VC specifically for auto layout (which I see you are using because you are setting your UIButton auto resizing mask). As long as you are declaring your objects in the main subclassed VC, this removes the "verbose" nature of auto layout into it's own file.
For multi-developer projects and/or true "reusable" code, a final thing you can do is move code into a Framework target.
I want to practice creating simple apps using no storyboard. I am able to do the constraints programmatically (slowly) but I want to also practice separating my code into MVC. Is there a particular place/method that I am supposed to write the programatic constraints? Or does it not matter?
Good discussion in the comments. My thoughts, based on that discussion?
With an understanding that the question is subjective, you place your constraints:
The earliest in a view controller's life cycle where they work.
As "close" to the view as possible.
If it's something common, make it as universal as possible.
Understand how your specifics fit into everything.
(Understand, the question isn't limited to constraints. It could apply to hierarchies, UI, even database tables when you get down to it!)
Sticking to constraints, and my answer....
(1) Use the UIViewController and UIView lifecycles.
Generally the view life cycle is loadView, viewDidLoad, viewWillAppear, viewWillLayoutSubviews, viewDidLayoutSubviews, and viewDidAppear. great SO answer detailing this.
I believe that loadView is too early for constraints, but not viewDidLoad - **provided you aren't expecting to know the frame size. While many say viewDidLayoutSubviews is the right place for that, I've found that viewWillLayoutSubviews most times works just as well. Either way, get your constraints set as soon as possible!
(2) Do it as close to the view as possible.
If you have subviews - I have a "ToolBar" class of objects - you want the constraints, at least as much as possible, to be coded inside the class. For instance, in my tool bar, it slides out, has buttons, and even rotates upon orientation. The only constraints not inside these classes is for orientation - that owner is (and I believe should be) the view controller instantiating it.
(3) Make it universal.
I plan to use this tool bar across a few apps. So the first thing I did was add it to a framework. This framework was needed because I had an app that I delivered a photo editing exension - and the "edit" screen is as much the same as possible. In the end I move all my constraints there. (At least as much as possible.) Anything that I believe is reusable.
(4) Understand the specific requirements of your app.
This should be obvious. If you need to code for various orientations, use arrays and activate/deactivate them. (YES, a common mistake is replacing them! That's setting yourself up for some major headaches.)
If you can keep things active, declare the constraint, set `isActive = true1, and forget about it. If you need to adjust that constraint's constant or multiplier, in the declaration name it and then where you need to alter it, do it.
My conclusion? Auto layout is a very useful tool - more so in code. But the placement of code is like asking "how does one code an OOP app for auto rentals" or " how does one design a database for auto rentals". It not just an art, there are many answers. These are the "rules" I try to follow - YMMV.
To get started with this style of development I recommend checking out Let's Build That App as he goes through very in-depth examples of setting up complex apps entirely in code, without storyboards.
The way he structures the constraints is using a custom implementation of UIView, that way your view code is separated from the ViewController. Then, in the viewDidLoad method you can instantiate your implementation of UIView with something like self.view = MyView().
I wrote a few apps like this. The major drawbacks are that it can become very difficult to make quick adjustments, and you really need to learn about all the different types of constraints you can use.
Here's a pastebin of some extensions I used when doing this. I hope this helps.
I'm new to programming, and I'm trying to understand this concept in Swift IOS. What are the benefits of HAVING to use IBaction and IBoutlet to connect things like UIButtons and UILabels to my code?
Why don't they just let us set UI objects equal to a name like button1 or label1 so we can use those names to call and mutate them in my code?
You don't. IBAction and IBOutlet is how storyboard and xib files created with Interface Builder (IB) link to the implementation files when unarchiving the XML dictionaries of the nibs. However, creating views and controllers with layouts in code is entirely permitted and even a common pattern for project management in teams.
Personally, I do like using Interface Builder for the visual aspect of laying out my views, and it helps reduce the size of my controller files because it allows me to put my layout and color settings into Storyboards and xibs. But, some developers will argue this is actually a drawback, since it obfuscates some of the functionality of your controllers from the uninitiated. There are strong arguments for avoiding the use of Interface Builder when working in teams, but it really boils down to strategy and preference.
They're just tags Xcode uses to link the code and the storyboard / XIB. Functionally they do nothing. They help you as the developer to know what is / isn't / can / can't be connected between the visual representation of your UI and the code driving it.
Since I've seen many other IOS projects written in obj-C, I found that many of them do not use interface builder or storyboard file. It's hard to see what's going on. And I've known that loadview method should do the things similarly to drag and drop objects in IB. So what is the difference between loadview method and doing some dragging objects in interface builder?
Well, you don't have AutoLayout (unless you use hard code to constraints too) and Size Classes without interface builder.
Working with IB is much less coding and more clarity.
If you add a Label to IB then the IB is responsible to release that object. And it's added to the view of course so you don't have to.
I would never go without IB now and would recommend to do so for other devs.
I use Size Classes + AutoLayout and all my screens looks good on every device plus that iOS9 is coming out: I have multiple screens auto enabled because I used Size Classes. App works without maintenance for many years..
Simple difference between IB and loadView
IB:
All u can do is drag and drop things without having any code written.
loadView:
It is first viewCycle method which calls on loading any view of corresponding viewcotroller, You should do all your stuff programmatically here. like adding subviews to your view. and adding constraints etc.
Thanks
I have lots of Views in old projects that I have layed out in code, using absolute positioning. That code is really hard to maintain. I'd like to port the code to using interface builder, but I'd have to layout every view again by hand.
Is it possible to programmatically create a XIB file from an existing UIView (which has been created programmatically)? I have searched the docs but can't find anything.
XIBs are essentially XML files, I guess that if you inspect a few you'd understand the format for different controls, but that would need a lot of manual reading, writing & testing to complete for all views. Probably a better plan would be to start slowly migrating to IB-made views.
Yes, it's possible; however, the thing is, you need to set all essential controls and properties of your xib. You won't be able to see its actual look and feel unless you have executed the project in your simulator or on an actual device (that means hard code everything).
You would use loadview in initializing your xib. Here's a bit of it:
- (void)loadview{
// set your view, screen size, and other properties of your xib
}