Adding a switch to UITableViewCell: storyboard vs code - ios

I want to add a UISwitch to my settings view controller. The switch looks identically to Airplane Mode toggle in the 'Settings' app on iOS. I'm not sure about what is the best way to implement this.
I am choosing between:
adding a UISwitch in code
creating a custom cell in the Storyboard and creating an outlet for a switch
Here's my Swift code for adding a switch to UITableViewCell:
let soundSwitch = UISwitch(frame: CGRectZero)
soundSwitch.addTarget(self, action: "test:", forControlEvents: UIControlEvents.ValueChanged)
// I created an outlet for the cell that will contain a switch
soundCell.accessoryView = tickingSoundSwitch
What are advantages and disadvantages of using these solutions?

My suggestion is to use either
1.UISwitch in storyboard along with an IBAction , if you use this approach things becomes easy ,you need not to add target and mess with lot of codes.
or
2.Completely rely on code, Using this approach your UISwitch will not get tight coupled with storyboard's UI. Even in the future if you want to change the UI stuffs in storyboard you need not to do any changes for your UISWitch ,i.e you don't need to attach all those IBAction and outlet stuffs as you are using pure code based approach(provided your UISwitch requirement doesn't change).

Related

My ViewController is getting really big due to UI coding

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.

Access elements in a custom view component in Swift 3 UI testing

I'm a newbie to any type of iOS programming. I am trying to write UI test cases for one of my scenes.
Following is the code I'm getting when I use recode method and tap on the custom component.
let button = XCUIApplication().children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .button).element
In this custom component there are two buttons. I wanna know which button is selected. For that I need to identify the button. But i'm getting same code where ever I tap on the custom view.
How can I access each component inside custom view. Any help would be great.
Add an accessibility identifier to your custom view in the app's code.
let customView: UIView!
customView.accessibilityIdentifier = "myCustomView"
Then access the contents like this:
let app = XCUIApplication()
let customView = app.otherElements["myCustomView"]
let button1 = customView.buttons.element(boundBy: 0)
let button2 = customView.buttons.element(boundBy: 1)
XCTAssertTrue(button1.isSelected)
XCTAssertFalse(button2.isSelected)
Note that to make your test deterministic, you should already know which button(s) should be selected. This ensures that your test tests the same thing every time it is run.
You need to make you elements visible to Accessibility.
I would suggest you to watch the WWDC Session about UI Testing in Xcode especially this part

Hide Outlets from Connections Inspector for Custom View

I have created a custom view (Quantity View) with nib file in Swift. I have created some IBOutlets & IBActions (for buttons, labels etc.) in my custom view.
I tried to use this custom view (Quantity View) by assigning class name to a UIView in my storyboard.
It's showing me all the IBOutlets & IBActions in the Connections Inspector, as shown in this screenshot: .
I just want to show only delegate for the Custom view.
Possible Answer:
I thought I can use the -viewWithTag to get the views instead of Outlets.
But, I want to know if it's possible with having Outlets also or if there is much better way to do this?
What are the other possible ways (optimum) to handle this situation?
You can also consider the following solution:
You can take the subviews of your QuantityViews(custom view) and you can identify the specific views by its frame origin.
Note : you should know the customview subviews frame
Its not possible to hide IBOutlets from storyboard if you declare the class members as IBs (IBOutlets or IBActions).
The IBOutlets or the IBActions are just indicators to the interface builder so that it can show the names on it when you try to bind them it actually calls the setValue: forKey: method to set the view's reference to the IBOutlet property.
Now if you try to access an subview from the file's owner class without any IBoutlets you need to have a pointer to point it, so for that either you can get the reference using ObjectID which is assigned to the subview by the interface builder or you can get it using the viewWithTag: method.
The ObjectID you need to find all time when you add or replace a subview from the view, so better and convenient approach is to use tag property of UIView class.
So my conclusion to this problem is to access the views using the viewWithTag method you mentioned earlier.
I think your way is correct. But sometimes Xcode doesn't work correctly.
The following makes the IBOutlets and IBActions reappear and work properly:
Clean project your project in Xcode.
Quit Xcode completely.
Delete all contents of ~/Library/Developer/Xcode/DerivedData/.
Restart MacOS just in case.
I hope you will resolve that :)

How can I add Apple's source code for UITableViewCell to my project?

Before you tell me that all I need to do is import UIKit, I know all about importing, and NO, that's not what I need to do in this case. Intrigued? Confused? Read on...
I have two different, but similar, custom UITableViewCells. Both have a UILabel and a UISwitch. Version one, DisplayCell, has a second UILabel, while version two, EditCell, has a UIPickerView. How I use them is like this, in a static UITableView that I'm using as a fill-in-the-data form, DisplayCell is the standard view which displays the selected value. The user can tap on DisplayCell to replace it with EditCell, then use the UIPickerView to pick a new value and hit done (button in the nav bar at the top). DisplayCell is then brought back, displaying the newly selected value. In either version the user can tap the switch to toggle whether or not the value from the UIPickerView should be used elsewhere in the form.
When it came time to write the code for the tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> (UITableViewCell) function I decided to rewrite my code so that I have a single class, SwitchCell that inherits from UITableViewCell and contains the IBOutlet and IBAction for the switch, then have DisplayCell and EditCell inherit from SwitchCell.
This works fine, however DisplayCell is now nothing more than a IBOutlet for a UILabel, and UITableViewCell, which DisplayCell inherits from via SwitchCell already has two UILabel IBOutlets, textLabel and detailTextLabel. The whole purpose in creating SwitchCell was to try and minimize code by only ever write any block of code once, something that I'm a bit of a fanatic about. Thus I would very much rather have the UILabel in DisplayCell use the detailTextLabel IBOutlet from UITabelViewCell, rather than having to create a 'redundant' UILabel IBOutlet for it.
In order to link to an IBOutlet in a superclass you must be able to bring up the code for that superclass in the assistant editor. Then you can just control+drag and link like normal. Which means, in theory, I believe it should be possible to link my UILabel to UITableViewCell's detailTextLabel, if I can access the appropriate line from the source code for UITableViewCell in the assistant editor. Is this possible, and if so, how?
Oh, and I'm working exclusively in Swift in this project, FYI.
tl;dr: It's really not anymore efficient to use UITableViewCell's default UILabels than it is to just add your own.
In order to link to an IBOutlet in a superclass you must be able to bring up the code for that superclass in the assistant editor.
Well, that's not correct. The assistant editor is not required to link to an IBOutlet defined in a superclass. But that's not important here anyway.
You can't link to detailTextLabel in Interface Builder because it's not defined with the #IBOutlet attribute. And although it is possible to override properties in Swift (so you can add #IBOutlet to it), that won't work in this case because an IBOutlet has to be mutable, and the superclass has defined the variable as immutable.
Now you could overcome this by adding your own setter method in the subclass to make the property mutable. I was able to do this with the following code:
var _detailTextLabel: UILabel?
#IBOutlet override var detailTextLabel: UILabel? {
get {
return super.detailTextLabel
}
set {
_detailTextLabel = newValue
}
}
I could wire this up in Interface Builder just fine. So perhaps I could tweak this code to actually get an set what I want (I don't think it would work as shown here). We're so far outside the realm of common-sense coding that we just need to stop and give up on this idea.
Thus I would very much rather have the UILabel in DisplayCell use the detailTextLabel IBOutlet from UITabelViewCell, rather than having to create a 'redundant' UILabel IBOutlet for it.
I can relate. I'm just as anal. :-)
However, if you do a little testing (or read the UITableViewCell header file) you'll see that UITableView is smart enough (optimized enough?) to not actually add a UILabel to the content view unless you try to use it. So at worst you have an unused property.
So it's really not inefficient to just add your own UILabel and property.

Where to customise IBOutlets

Where should I customise my IBOutlets?
Say I have created a button with interface builder, created an IBOutlet for it and I would want to change a property during runtime (ex: background color or localized title).
I would think of adding it to the viewDidLoad method, but outlets aren't yet created.
I remember having nil outlets in viewDidLoad, but I might be wrong.
If I move it viewWillAppear, the code will be executed every time the view controller's view appears.
Is there any better place for my IBOutlet related code, so it's only executed once?
Obviously I can do just about any customization using only the interface builder and making use of the User defined runtime attributes or localized stroryboards, but I don't like that since it's much more tedious to change later.
From the Doc
Its clearly says about the Views loaded into the memory in the -viewDidLoad() delegate itself.
I would think of adding it to the viewDidLoad method, but outlets
aren't yet created.
It is a false statement, Because you only get the viewDidLoad: message after IBOutlets are created. So you can safely do any customization in viewDidLoad:
Let’s say you have a Button you want to customise. You put the button at the place where you want it to be and then open the “Identity Inspector” on the right.
There is a textfield for “Custom Class”:
I usually create a subclass of UIButton / NSButton (depending on iOS or OSX) and edit the behaviour, drawing methods and functionality in this class file. Then just add the name of this class in this textfield. Voila!

Resources