I'm new to iOS development and not sure how iOS connects a UI object to the underlying view controller object. In Android the UI element has an id set in the designer and used in the view but I don't see the same in Xcode only the following (in the ViewController.swift which doesn't seem to connect to a specific UI element):
#IBOutlet weak var statusLabel: UILabel!
where as in Android I'd do the following in OnCreate()
statusLabel = (TextView)findViewById(R.id.statusLabel);
To change the variable name of the outlet once it's connected, Xcode 9.2 and higher has a refactor feature, which allows you to rename variables easily.
Command + Click on the variable name and then click Rename....
Each instance of the UIViewController class has an underlying UIView property called view. When you are using interface builder, you can ctrl+drag the view you have added to create an IBOutlet which references the view whose properties you have modified in interface builder.
This creates the UI element and will add it as a subview of UIViewController's view and allow you to access it directly in any of the UIViewController's methods, or (assuming its not private) any instance of that UIViewController.
let myViewControllerInstance : ViewController = UIStoryboard.init(name: "main", bundle: nil)!.instantiateViewController(withIdentifier: "ViewControllerStoryboardID")
myViewControllerInstance.myStatusLabel.text = "I can change the label's properties from any instance of the View Controller"
If you need to programmatically modify any properties of your UIView instance before the ViewController appears it is recommended to do so in ViewDidLoad.
class ViewController : UIViewController {
#IBOutlet weak var statusLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
statusLabel.text = "My Status Label" //Accessing your UI element directly
}
}
The alternative to using the storyboards approach would be to instantiate the view programmatically, set it's frame (or add constraints if using auto-layout), and add it as a subview.
class ViewController : UIViewController {
var myStatusLabel : UIView
override func viewDidLoad() {
super.viewDidLoad()
statusLabel = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
self.view.addSubview(myStatusLabel)
statusLabel.text = "This label was created progrommatically."
}
}
In iOS you usually use Storyboards (or sometimes XIB files) to set up your user interface. In a storyboard/XIB you control-drag from your interface into your view controller's code to create or connect outlet links.
The storyboard/XIB is an XML file internally that describes the UI and the links to outlets. At the time the storyboard/XIB is loaded, the system connects the outlet and action links.
Related
I want to access UIElements (e.g Labels and TextFields, etc) of viewController without making IBOutlet connections.
e.g. I have a UITextField in viewController and I want to access it like viewControllerName.textFieldName.text = "something I will set here"
or as the same concept of Android findViewById("id of element")
I have used "Tags" but it does not meet my requirement.
You will create UITextField programmatically, and access the UITextFieldName.text = "Some" programmatically.
Create a label programmatically Check This link
Yes, you can access UIElements(e.g Labels and TextFeilds etc) of viewController without making IBOutlet connections. First give UIElement a Tag.
Then try this. hope this would help:
If you want to access it in the same viewController
let label = self.view.viewWithTag(4) as? UILabel
label?.text = "Hello there"
and if you want to access it from other viewController
in firstViewControler
import UIKit
internal weak var FirstViewController: ViewController?
//in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
FirstViewController = self
}
it will make it accessible in all other viewControllers
then in secondViewController
let newlabel = homeViewController?.view.viewWithTag(4) as? UILabel
newlabel?.text = "new change"
I am currently trying to build a demo app in Xcode 8.2.1 using swift to learn how to use UIPageViewController. I added three outlets to my subclass of UIPageViewController which I intended to connect to the three views that the page view controller would control.
class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource
{
#IBOutlet weak var redController : MyColorViewController!
#IBOutlet weak var greenController : MyColorViewController!
#IBOutlet weak var blueController : MyColorViewController!
//...
}
In my main storyboard, I created one instance of MyPageViewController and three instances of MyColorViewController.
But, for the life of me, I cannot figure out how to get the storyboard to connect the three outlets above to any of the color view controller instances.
I have tried both ctrl-dragging from the page view controller itself to each color view controller and from the page view controller's outlets in the inspector. Nothing works.
Suggestions?
You should do this by code. And not with IBOutlets.
First: set a storyboard ID to each of the UIViewControllers.
Second:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"blueViewController") as! UIViewController
And connect it to a property.
You do this for all three UIViewControllers.
I'm building an app (in XCode 8.2.1) where some objects are displayed on a 2D board, and when the user taps one of these objects some info should be displayed about it as a styled modal info box. My design is to have the info written in a separate view controller, which I would display when needed.
I've designed a basic stub for the second view controller and added a single label to it in the interface builder. Then I've ctrl-linked this label to my custom VC class:
class InfoViewController: UIViewController {
#IBOutlet weak var info: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func displayInfo() {
info.attributedText = NSAttributedString(string: "abc")
}
}
However, when I test my app and tap the object, the info field is nil even in the viewDidLoad() method of my custom VC class. The way I'm displaying my VC is as follows:
let infoViewController = InfoViewController()
infoViewController.modalPresentationStyle = .overCurrentContext
self.present(infoViewController, animated: true, completion: nil)
infoViewController.displayInfo()
(Note: In the end I will have only one single instance of InfoViewController but this is just for testing. I don't expect having a global instance would make any difference?)
As I said, be it inside the viewDidLoad() method or in the displayInfo() method, info is always nil, such that setting its attributedString attribute crashes the app. Thinking the present method might be called asynchronously, I've tried calling displayInfo() from inside viewDidLoad(), but that didn't make any difference.
Can anyone tell my what I've forgotten that would allow my IBOutlet from being properly initialized properly?
Thanks!
David
The problem is the reference to InfoViewController(), which instantiates the view controller independent of any storyboard scene. You want to use instantiateViewController:
let infoViewController = storyboard?.instantiateViewController(withIdentifier: "Info") as! InfoViewController
infoViewController.modalPresentationStyle = .overCurrentContext
present(infoViewController, animated: true) {
infoViewController.displayInfo()
}
A couple of notes:
This assumes that (a) you've given the scene in the storyboard a "storyboard id"; (b) you've set the base class for that scene to InfoViewController.
Note, I called displayInfo in the completion handler of present because you probably don't want that called until the scene has been presented and the outlets have been hooked up.
Alternatively, you can update non-outlet properties of the InfoViewController immediately after instantiating it and then have its viewDidLoad take those properties and update the outlets, e.g.:
class InfoViewController: UIViewController {
var info: String!
#IBOutlet weak var infoLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
infoLabel.attributedText = NSAttributedString(string: info)
}
}
Note, I changed the #IBOutlet name to be infoLabel and added the String property called info. That tends to be the convention, that outlets bear some suffix indicating the type of control, and model objects, like the String property, are without the suffix. (You'll just want to make sure you remove that old outlet in the connections inspector in IB so that you don't have problems with these property name changes.)
Anyway, you can then do:
let infoViewController = storyboard?.instantiateViewController(withIdentifier: "Info") as! InfoViewController
infoViewController.info = "abc"
infoViewController.modalPresentationStyle = .overCurrentContext
present(infoViewController, animated: true, completion: nil)
The key point is don't try to update outlets of the scene immediately after instantiating it, but make sure that this is deferred until after viewDidLoad was called.
I Replaced
let vc = CCDetailViewController()
With
let vc = self.storyboard?.instantiateViewController(withIdentifier: "CCDetailViewController")
Finally
self.present(vc!, animated: true, completion: nil)
Now It Works...
In my case, I had created new view controller for the same class. Which had ended up with two view controllers in storyboard, but referring to the same class. After deleting old view controller, everything worked fine.
Hope it helps to someone.
I have a UIViewController-class instantiated via Storyboard that contains a constant property. For testing, I want to replace/mock whatever the value of the view controller.
I can actually do this by subclassing and defining a new constant and by overriding the methods that use it. However, I do not know how to instantiate the ViewController, since it's not in the storyboard.
It's important that all views and all other functionality of the original ViewController is still present, of course. How to go about it?
If i understand you need to access a property from another ViewController outside your Storyboard without presenting it. Since you're using swift, all you need to do is instantiate the class itself i believe. For example if the ViewController that is not in the storyboard has a class named "SecondController", and the variable inside second controller is called "stringVar" then all you need to do is this:
var secondVC = SecondController()
secondVC.stringVar = "new string value"
Example:
//SecondVC
import UIKit
class SecondViewController: UIViewController {
var something:String! = "String Value";
}
//Main VC
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var secondVC = SecondViewController()
secondVC.something = "Another String Value"
println(secondVC.something)
}
}
I made a new view controller in Storyboard and made a new .Swift file that inherits UIViewController. However, my viewDidLoad() is not being called.
What do I need to do to connect my view controller in my story board to my .swift code so viewDidLoad() is called?
I see there's this: How to connect storyboard to viewcontroller
However, in a default project, FirstViewController(storyboard) doesn't have FirstViewController in that box, it is empty, yet viewDidLoad() is still called. It doesn't make sense.
Choose your ViewController in the storyboard or scene list (mine is called Circuit).
Click on the Identity Inspector on the right
Select Custom Class > Class > Type name of your swift file.
DONE :)
You have your ViewControler created with some Objects in it (UILabel, UIButton, UIImage...)
1 - You need to link your ViewControler in your story board to your ViewController.swift , to do this follow the pictures
2 - In the class filed put the name of the ViewController class. With that you just linked your storyBoard view controller to your viewController.swift class
Class = ViewController.swift
3 - Now you need to cretae the variables you want to asign(UILabel, UIButton ... that you have in your storyboard): in this example:
class MovieDetailViewController: UIViewController, DetailView {
var presenter: MovieDetailPresenter!
var movie: Movie?
#IBOutlet weak var lblMovieYear: UILabel!
#IBOutlet weak var lblMovieTitle: UILabel!
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var lblMovieRating: UILabel!
#IBOutlet weak var lblMovieOverview: UILabel!
}
4 - To link the UILabel in the story board to your UILabel variable or your UIButton in your story board to your UIButton var, follow the next images
First select the view controller in the storyboard
Second select the parragraf icon in the right up corner ands clicked 'Assistant', it will open a screen of your ViewControler.swift
Now you just need to drag the variable to the corresponding object and you will be done.
Remember to do this with all variables, you will need to create a variable for each object you have in the storyboard
Connect storyboard and view controller
You should set ViewController in Class` field