Getting a signal SIGABRT when using instantiateViewController with UIVewController - ios

I hope someone can help me with this problem I am getting with swift.
I am trying to add an array of UIViewControllers to a UIPageViewController. However, whenever I try to access a view controller through the method instantiateViewController, I get a SIGABRT error.
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
Here is my entire ViewController.swift file just for the reference.
import UIKit
class ViewController: UIViewController {
var pageViewController: UIPageViewController!
var views: [String] = ["view1","view2"]
override func viewDidLoad() {
super.viewDidLoad()
self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
var arr: [UIViewController] = []
for i in 0..<views.count{
arr.append(assignView(index: i))
}
self.pageViewController.setViewControllers(arr, direction: .forward, animated: true, completion: nil )
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
pageViewController.didMove(toParentViewController: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func assignView (index: Int) -> UIViewController{
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
return vc
}
}
Can someone tell me why my code is throwing me this error?
Thank you so much!

From the line below:
self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
It seems that storyboard is treated as optional. It may be that the storyboard variable is getting nil when you are trying to access it.Ideally it should not be nil and should not be optional.
If the optional is purposefully done then you can apply a "if let" to check before you access.
Let me know if you need further clarification.

SIGABRT is a controlled crash and the app terminated mostly on purpose. It mostly resulted from programmer's fault.
Look for following as there is two possible causes to crash.
self.storyboard doesn't properly got initialised as there is no corresponding storyboard or incorrectly referred.
View controller's identifier is incorrectly referred.

Consider the following example
var mainView: UIStoryboard!
mainView = UIStoryboard(name: "Main", bundle: nil)
let samplecontroller : UIViewController = mainView.instantiateViewController(withIdentifier: "iPhone")
In the above example mainView is my instance referring to StoryBoard which is implicitly unwrapped(this is done on purpose to specify that this cannot be nil)
samplecontroller is the instance of the scene in the storyBoard. "iPhone" is the name of the StoryBoardID of the scene.
This snippet will ensure that the mainView will never be nil provided my storyboard is setup correctly with the Ids and names used.
After this you can still have a safety check like
if let test = mainView {
//Do your stuff
}
Let me know if this helps.

Related

Error passing data between two ViewControllers when declaring the delegate within a UITabBarController

I’m trying to pass data between two ViewControllers with the initial call being made from a UITabBarController.
Here is what I’m doing. I’m using a class called RaisedTabBarController to add a custom button to a TabBarController, which works fine displaying the button, my issue is that when I tap the custom button I want it to take me to FirstViewController and then I want to pass data from FirstViewController to SecondViewController via protocols but for some reason I’m getting an error that in my opinion doesn’t make any sense, it complains about a labels not being accessible within SecondViewController.
Here is the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is the code…
Class ref from GitHub:
RaisedTabBarController
TabBarController
Here I'm adding the custom button and making the call to go to FirstViewController
import UIKit
/// TabBarController subclasses RaisedTabBarController
class TabBarController: RaisedTabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Insert empty tab item at center index. In this case we have 5 tabs.
self.insertEmptyTabItem("", atIndex: 2)
// Raise the center button with image
let img = UIImage(named: “myImage”)
self.addRaisedButton(img, highlightImage: nil, offset: -10.0)
}
// Handler for raised button
override func onRaisedButton(_ sender: UIButton!) {
super.onRaisedButton(sender)
// Go to FirstViewController
let pvc = storyboard?.instantiateViewController(withIdentifier: “firstStoryBoardID”) as! FirstViewController
/// Here, I’m not sure if this is the right way to tell that
/// SecondViewController will be the delegate not TabBarController, seem to work
pvc.delegate = SecondViewController() as FirstViewControllerDelegate
self.present(pvc, animated:true, completion:nil)
}
}
FirstViewController
From here I want to send data to SecondViewController
protocol FirstViewControllerDelegate {
func messageData(greeting: String)
}
class FirstViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
}
func sendData() {
self.delegate?.messageData(greeting: “Hello SecondViewController”)
}
}
SecondViewController
Here I want to receive the data sent from FirstViewController
class SecondViewController: UIViewController, FirstViewControllerDelegate{
#IBOutlet weak var labelMessage: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func messageData(greeting: String) {
/// I do get message from FirstViewController
print(" Message received from FirstViewController: \(greeting)")
/// Here I get error, fatal error: unexpectedly found nil while unwrapping an Optional value
/// I think it has something to do with the labelMessage not being accessible, but why?
labelMessage.text = greeting
}
}
Any idea why am I getting the error in SecondViewController, why wouldn't labels be accessible if they are declared in SecondViewController?
Ideally I would like to be able to call method onRaisedButton(_ sender: UIButton!) directly from SecondViewController but without having to subclass RaisedTabBarController. I’m not usr if this would solve the error but I think this would make my code cleaner.
EDIT: 06/19/2017 - Solved
The effect I was looking for can be done directly in XCode, in the storyboards. I stopped using the third party class (RaisedTabBarController), problem solved.
This seems wrong.
pvc.delegate = SecondViewController() as FirstViewControllerDelegate
Try to instantiate the SecondViewController like you did for the first from storyboard.
let svc = storyboard?.instantiateViewController(withIdentifier: “secondStoryBoardID”) as! SecondViewController
And then set the delegate to SecondViewController
pvc.delegate = svc

UISplitViewController as child of custom tab bar controller

I've been following this tutorial to create a custom tab bar controller for an iPad app as I would like to implement a vertical tab bar. However, I would like one of the tabs to present a UISplitViewController, whilst the others just present UIViewControllers. My questions are:
1) Will this be accepted by the app store? Apple's documentation currently states adding UISplitViews as child views is not recommended but may be implemented with certain containers. Anyone had any experience with this?
2) Here is an extract from my custom tab bar controller. If secondViewController is presenting the UISplitView, can I leave it as is? I mean, it seems to work find when I run it, but is it acceptable?
class CustomTabBarController: UIViewController {
#IBOutlet weak var tabView: UIView!
#IBOutlet var tabButtons: [UIButton]!
var firstViewController: UIViewController!
var secondViewController: UISplitViewController!
var thirdViewController: UIViewController!
var viewControllerArray: [UIViewController]!
var selectedTabIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
firstViewController = storyboard.instantiateViewController(withIdentifier: "firstVC")
secondViewController = storyboard.instantiateViewController(withIdentifier: "secondVC") as! UISplitViewController
thirdViewController = storyboard.instantiateViewController(withIdentifier: "thirdVC")
viewControllerArray = [firstViewController, secondViewController, thirdViewController]
tabButtons[selectedTabIndex].isSelected = true
didPressTab(tabButtons[selectedTabIndex])
}
3) I can't really get my head around what (if anything) needs to go in AppDelegate? Again seems to run fine but just wondering if its safe.
Thanks.
1) I believe Apple is simply recommending against this as potentially bad design, since they refer you to the Human Interface Guidelines. You don't always have to agree with their recommendations and very rarely will your app get rejected for design choices- the only instances off the top of my head would be mimicking the App Store or other core OS functionality.
2) If, as you say, this is working, I don't see any glaring issue.
3) Again if it's working, you may not need to do anything. But here's how Apple sets up their template for a Master-Detail app:
If your splitViewController is set up like this and you want the same functionality as this template, here's how you should be able to get it.
First add this to the very bottom of AppDelegate.swift:
extension AppDelegate: UISplitViewControllerDelegate {
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
if topAsDetailController.detailItem == nil {
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return true
}
return false
}
}
Then, add this to the end of viewDidLoad in CustomTabBarController:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let navigationController = secondViewController.viewControllers[secondViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = secondViewController.displayModeButtonItem
secondViewController.delegate = appDelegate

Sending information between ViewControllers in swift

I have two classes:
class ExplorerViewController: UIViewController {
lazy var studyButton: ExploreButton = {
let button = ExploreButton()
button.setTitle("Study", forState: .Normal)
return button
}()
}
and
class ViewController: UIViewController, UISearchBarDelegate, LocateOnTheMap, GMSMapViewDelegate {
}
I'm trying to make it so that when I click the studyButton, it sends the button title to ViewController and goes to that view.
I'm not using storyboards and am having trouble with segues since every tutorial seems to give different examples that are specific to the things they've been working with and 95% of them seem to be operating with storyboard. Can someone give me a general way of how to do this?
How do I give the starting view controller an identifier because it isn't instantiated like the other controllers that I 'move' to after. How can I move from ViewController to ExplorerViewController and then move back to that same ViewController (with all changes intact).
Create an initializer for your ViewController that receives the "title" variable:
class ViewController: UIViewController, UISearchBarDelegate, LocateOnTheMap, GMSMapViewDelegate {
var btnTitle: String?
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?, btnTitle:String?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.btnTitle = btnTitle
}
}
When creating the ViewController object use this initializer.
var viewController = ViewController(nibName: "ViewController", bundle: nil, btnTitle: title
You can initialize UIViewController that you want navigate to, assign data to properties in that controller and call this method:
presentViewController(viewController, animated: true, completion: nil)
For example:
let destinationViewController = ViewController()
destinationViewController.frame = self.view.frame
destinationViewController.buttonTitle = "title"
self.presentViewController(destinationViewController, animated: true, completion: nil)
Although I would suggest you to get familiar with Storyboards and perform navigation with Segues.
Make sure of two things:-
1.) You have given your viewController an StoryBoard ID lets say "viewControllerVC_ID" in it's Identity inspector
2.) You have NavigationController Embed in to your Initial entry point View Controller
In ViewController declare a variable
var btnLabelTxt : String!
Create an #IBAction of that button in ExplorerViewController :-
#IBAction func exploreBtnAction(sender : UIButton!){
let vcScene = self.navigationController?.storyboard?.instantiateViewControllerWithIdentifier("viewControllerVC_ID") as! ViewController
vcScene.btnLabelTxt = "Study"
//or you can just access the button itself in the viewController and set the title
//By vcScene.yourBtn.setTitle("Study", forState: .Normal)
self.navigationController?.pushViewController(vcScene, animated: true)
}
please see this question How to push viewcontroller ( view controller )? for how to switch between views.
to pass data once you have reference to the new view, you can assign the data to a property of that view.

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

Can't cast value of type UIViewController to PatternDetailViewController

Am trying to downcast a view controller to a detail view controller but can't. Am using Core Data (for the first time).
The error is in the prepareForSegue method and reads: "Could not cast value of type 'UIViewController' (0x1b81cdc) to 'Patternz.PatternDetailViewController' (0x32488).
(lldb) "
Would appreciate an explanation of why it doesn't work.
Here are the files.
ViewController.swift
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var patterns : [Pattern] = []
var selectedPattern : Pattern? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.dataSource = self
self.tableView.delegate = self
createTestPatterns()
var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var request = NSFetchRequest(entityName: "Pattern")
var results = context.executeFetchRequest(request, error: nil)
if results != nil {
self.patterns = results! as! [Pattern]
}
}
func createTestPatterns() {
var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var pattern = NSEntityDescription.insertNewObjectForEntityForName("Pattern", inManagedObjectContext: context) as! Pattern
pattern.name = "Dress Shirt"
pattern.frontimage = UIImageJPEGRepresentation(UIImage(named: "examplePattern.jpg"), 1)
context.save(nil)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.patterns.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
var pattern = self.patterns[indexPath.row]
cell.textLabel!.text = pattern.name
cell.imageView!.image = UIImage(data: pattern.frontimage)
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedPattern = self.patterns[indexPath.row]
self.performSegueWithIdentifier("patternDetailSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "patternDetailSegue" {
var detailViewController = segue.destinationViewController as! PatternDetailViewController // Could not cast value of type 'UIViewController' to 'Patternz.PatternDetailViewController'
detailViewController.pattern = self.selectedPattern
}
}
}
PatternDetailViewController.swift
import UIKit
class PatternDetailViewController: UIViewController {
var pattern : Pattern? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.navigationItem.title = self.pattern!.name
}
}
The problem, as you have said, is in these lines:
if segue.identifier == "patternDetailSegue" {
var detailViewController = segue.destinationViewController as! PatternDetailViewController
// Could not cast value of type 'UIViewController' to 'Patternz.PatternDetailViewController'
The error message tells you that the destinationViewController of this segue is not, in fact, a PatternDetailViewController. You may think it is, but it isn't. You need to examine this segue in the storyboard and see what's really at the destination end of it.
The fact that the error message describes it as a UIViewController makes me suspect that you forgot to enter any view controller type in this view controller's Identity inspector in the storyboard:
Adding to matt answer. Sometimes, you may also need to double check the custom class Module from the Identity inspector.
Your view controller need to belong to a specific Module. So make sure to select a Module from the list. In my case:
It looks like you are using a navigation controller judging by your viewDidLoad() in PatternDetailViewController.
If PatternDetailViewController is embedded in a UINavigatonController then the navigation controller will be segue.destinationViewController.
Get the PatternDetailViewController like this:
let vc: UINavigationController = segue.destinationViewController as! UINavigationController
let detailVC = vc.topViewController as! PatternDetailViewController
detailVC.pattern = self.selectedPattern
This issue can happen if you copy Storyboard files from other project and did not change the Module .
In the above image "EmployeeApp" is the old project name and the "clientTradingApp" is the new project name
Happy Coding :)
I solved the issue by following these steps:
Find the source Scene in the storyboard. Go to Identity Inspector (Third tab in the right panel)
Remove (Cut/ctrl+x) the "Class" under "Custom Class" from the Identity inspector.
Switch to another scene.
Switch back to source scene.
Paste the custom class module you just removed.
Exit XCode.
Open Xcode.
Clean.
Build.
Why did it work? I was copy-pasting someone else's work. I probably wrote Class name in the right hand panel before there was a class with that name. My guess is Swift can not find these kinds of things dynamically. You have to behave like it wishes. You have to do certain things in a certain order.
This just happened to me and I struggled for a couple hours following all the other posts here. In the end it turned out that my destination view controller file was missing the .swift extension. I hope this helps someone else!
My issue is:
Not work
let aBCViewController = UIViewController(nibName: "ABCViewController", bundle: nil) as! ABCViewController
present(aBCViewController, animated: true, completion: nil)
Work
let aBCViewController = ABCViewController(nibName: "ABCViewController", bundle: nil)
present(aBCViewController, animated: true, completion: nil)
Try clean
Product > Clean
Edit: I came across the same error message and it was fixed using this method. This answer may not specifically answer the question

Resources