Update external XIB objects programmatically in Swift - ios

I have two XIB views loaded that are accessed through swipe gestures and would like to update my second XIB's textbox from my first XIB.
Here is the swift code I have so far but it appears that it doesn't work from one view to the other:
// This code is placed in ViewController0.xib
let vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
vc1.resultsTextBox?.text = "test"
// vc1.asd(1) //Tried calling a function that's in that view but it didn't work either.

If resultsTextBox is a UITextField then it is likely not yet initialized. That is done in viewDidLoad. The better strategy is to set some string variable to "test". Then in viewDidLoad, set the resultsTextBox.text
let vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
vc1.textBoxStr = "test"
override func viewDidLoad() {
super.viewDidLoad()
vc1.resultsTextBox?.text = textBoxStr
...
}
Hope that makes sense.

Related

Pass data from Parent UIviewController to a Container (ChildViewController) every time the value is changed

I have a UIViewController that has two containers and each Container is related to a UIViewController for some specific functionalities.
For people who are devaluating my question, it will be more helpful and appreciated if you put me on the right path instead.
what I am trying to do is pass data from the parent ViewController to the childViewControllers
I tried it using the protocol/delegate: But the problem is, I
couldn't assign the delegate to the childViewContainer since it
doesn't have an instance from the parent.
My second try was using the prepare function, but it doesn't work as well since the two containers load once the parent loads at first. so if the value is changed in the parentViewController I can't pass it again to the child.
Any Idea, please?
After a deeper digging I was able to find a solution for my own question.
here I am going to post if anyone else needs it in the future
so, first of all, I need it to lunch the ChildContoller from the parent controller and not from the storyboard ( so I deleted the segue between the parent and the child.
create a variable for childController like that:
lazy var firstChildViewController: FirstChildViewController = {
let storynoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storynoard.instantiateViewController(identifier: "firstChild") as! FirstChildViewController
self.addChild(viewController)
self.view.addSubview(viewController.view)
return viewController
}()
same thing for the other one if you have two children
and then in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
firstChildViewController.view.isHidden = false
secondChildViewController.view.isHidden = true
}
and then in the FirstChildViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let parent = self.parent as? ParentViewController {
parent.delegate = self
}
}
And the problem is solved
Hope it helps someone

Swift: UI Elements equal nil when programatically creating split view controller

I am programmatically creating a split view controller using the following code when a table view cell is touched:
let rootViewController: UIViewController = RootTableViewController()
let navVC: UINavigationController = UINavigationController(rootViewController: rootViewController)
let detailViewController: UIViewController = DetailTableViewController()
let splitVC: UISplitViewController = UISplitViewController()
splitVC.viewControllers = [navVC, detailViewController]
self.present(splitVC, animated: true, completion: nil)
but when I tap the tableViewCell I get the error: 'fatal error: unexpectedly found nil while unwrapping an Optional value' which appears to be linked to a UITextField (and all UI Elements) on the RootTableViewController. The first failure is in the viewDidLoad of RootViewController after executing the above code when a value is passed to a ui element
Where exactly does this happen? Wheres the error originated? I had a similar issue where I tried to access IBOutlets before they were created by the system which caused my app to crash.
Specifically I had a UI update function which was called after setting a property of the ViewController.
I got around this by checking for nil in the update function and since it was called before viewDidLoad was called the first time, I called the update function in viewDidLoad manually to make sure that when it shows for the first time, everything is correctly updated.
UPDATE
I think I have an idea of whats going on. You created a Storyboard, setup your UI and chose your ViewControllers as the classes of the ViewControllers in the Storyboard.
If you now want to use the ViewControllers, you have to instantiate them via the Storyboard rather than manually initializing them (something like this):
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
You also could use Segues instead of instantiating something at all. Build your complete UI using the Storyboard and then use a Segue to present the SplitViewController.
The last method I can think of is, that if you want to instantiate the ViewControllers manually and still make use of the Storyboard or a nib, you have to do some custom initialization in in the init functions of your ViewControllers (this code is from a custom view I have in a separate .xib):
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
UINib(nibName: "DatePickerKeyboard", bundle: nil).instantiate(withOwner: self, options: nil)
addSubview(view)
view.frame = self.bounds
}

Swift/iOS: IBOutlet nil after loading view controller

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.

viewdidload is not calling

I have a view controller which is on my story board. The following is the code for view controller;
class SingleLineGraphController: UIViewController {
#IBOutlet var lineGraph: LineGraphView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark: GraphDelegate implementation
func plotLineGraph(xAxisValue:[NSDate],yAxisValue:[Double],displayView:GraphDisplayView, graphTitle:String,graphStartDate:NSDate , graphEndDate:NSDate)
{
lineGraph.plotLineGraph(xAxisValue, yAxisValue: yAxisValue, displayView: displayView, graphTitle: graphTitle, graphStartDate: graphStartDate, graphEndDate: graphEndDate)
}
}
now i am accessing this view controller from another view like this;
let mainStoryboard = UIStoryboard(name: "Menu", bundle: NSBundle.mainBundle())
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController
let graphData = getGraphData(DashBoardRow.Respiratory, cellTitle: "Respiratory") as! LineGraphModel
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
self.navigationController?.pushViewController(navigationController, animated: false)
The problem is when i instantiate the SingleLineGraphController from story board it doesn't call the viewdidload and hence the lineGraph becomes nil until the point where i call
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
and hence it gives me an exception. I have commented out that line and put a breakpoint on viewdidload and find out that once the below line is executed , it loads the lineGraph.
self.navigationController?.pushViewController(navigationController, animated: false)
Does anyone have any idea how can i force the viewdidload before the above line so that my method doesn't crash.
In your line:
self.navigationController?.pushViewController(navigationController, animated: false)
You are pushing a navigationController, not your singleLineGraphController. Are you sure this is correct or did you leave some code out?
Even if you present the controller correctly, because you trying to plot your data before the view is presented the view is not accessible yet. One way to correct this is to set your data to plot on the controller as a property and only plot that data in your viewDidLoad when you know for sure the view is available.
You could access the view property to force the view to load before the controller is actually presented but this is a wrong solution to this problem.
if you will not push your VC to NavigationController, its viewDidLoad will not called
viewDidLoad will only called before coming on to the screen,
Then viewWillAppear and then viewDidAppear.
VC will only come on to the screen if you push or present or set it as rootVC of NVC and load that NVC as rootVC of window or set it directly to rootVC of window
Just call:
singleLineGraphController.view
After this line:
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController

Swift need to reload TableView that is contained within a containerView

I have a tableViewController with in a containerView in the main view of my app. The user can elect one of three buttons which needs to pass a new value to the tableViewController and reload the tableView.
I can't seem to find an example of how best to do this programmatically.
Any suggestions or links to examples thanks.
Adding some code of what I have tried
#IBAction func checkins10(sender: AnyObject) {
statusImage10.hidden = false
statusImage1.hidden = true
statusImage5.hidden = true
self.hourWindow = 10.00
reloadCheckInView()
}
func reloadCheckInView() {
let checkInsTableViewController: FriendCheckInTableViewController = FriendCheckInTableViewController(nibName: "FriendCheckInTableViewController", bundle: nil)
checkInsTableViewController.checkInsTableView.reloadData()
}
Updated
func reloadCheckInView() {
// let checkInsTableViewController: FriendCheckInTableViewController = FriendCheckInTableViewController(nibName: "FriendCheckInTableViewController", bundle: nil)
var viewControllerStoryboardId = "FriendCheckInTableViewController"
var storyboardName = "Main"
var storyboard = UIStoryboard(name: storyboardName, bundle: NSBundle.mainBundle())
let checkInsTableViewController = storyboard.instantiateViewControllerWithIdentifier(viewControllerStoryboardId) as UIViewController!
You're getting a new instance of FriendCheckInTableViewController by loading it from a bundle. You need to have a reference to the one in the storyboard in your containing view and set up the connection when editing the storyboard.
You need to create a var for the tableview and connect it in your storyboard.
#IBOutlet weak var checkInsTableView: UITableView?
Connect it to your tableview in the storyboard and just refresh data as needed.
self.checkInsTableView.reloadData()

Resources