When loading a UIViewController programmatically in iOS 8.4 I am consistently seeing the IBOutlets registering as nil. This is causing crashes on any iOS 8.4 device. The exact same code runs smoothly on iOS 9 and 9.0.1. For reference, this is a snippet of the view controller
class B8AVPermissionsViewController: B8BaseViewController {
#IBOutlet weak var closeButton: UIButton!
#IBOutlet weak var cameraButton: UIButton!
#IBOutlet weak var microphoneButton: UIButton!
var delegate: B8PermissionRequestingDelegate? = nil;
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated);
NSLog("cameraButton: \(cameraButton)")
}
That prints out cameraButton: nil
The code that creates this looks like:
self.permissionViewController = B8AVPermissionsViewController()
self.permissionViewController!.delegate = self
dispatch_async(dispatch_get_main_queue(), {
self.presentViewController(self.permissionViewController!, animated: true, completion: nil)
})
What am I doing wrong?
The problem is that there's bug in iOS 8. Saying B8AVPermissionsViewController() in iOS 8 does not automatically load the associated nib. You need to work around this; for example, you could call init(nibName:bundle:) explicitly and tell it where the nib is.
The bug is fixed in iOS 9, which is why you're not seeing the same problem there.
Related
I am using the following UIView extension:
https://github.com/snoozelag/GoneVisible
I have successfully downloaded the file and added the Swift file and I am using the extension to hide (gone method) and show (visible method) buttons on the navigation bar. When the app first opens, I call this extension in an attempt to hide certain buttons if the user is already logged in. However, this has not been working. Strangely, it DOES work and hides the buttons after I segue to a different view and go back.
Here is the code:
import UIKit
import Parse
class ViewController: UIViewController {
#IBOutlet weak var signUpButton: UIButton!
#IBOutlet weak var logInButton: UIButton!
#IBOutlet weak var myAccountButton: UIButton!
#IBOutlet weak var bigGame: UIImageView!
private func setUpPage(){
let currentUser = PFUser.current()
if currentUser != nil {
// Do stuff with the user
self.myAccountButton.visible()
self.signUpButton.gone()
self.logInButton.gone()
} else {
// Show the signup or login screen
self.myAccountButton.gone()
self.signUpButton.visible()
self.logInButton.visible()
}
}
override func viewDidLoad() {
setUpPage()
super.viewDidLoad()
self.navigationItem.hidesBackButton = true;
}
override func viewWillAppear(_
animated: Bool) {
setUpPage()
}
My question is, how can I get this extension to fire when the app is first opened?
Thanks a lot for your help :)
Don't forget to call super.viewWillAppear(...) when you override the inherited implementation.
This might solve your issue - but even if not it is correct to do it.
Update:
try calling setUpPage() only once and only after you call super.viewDidLoad()
I'm relatively new to programming and this is my first attempt at developing an iOS without following a tutorial, so please bear with me.
I have a custom table view called 'CupboardViewController' that returns four string labels based on a custom UITableViewCell class called 'ItemTableViewCell'. I want to be able to show those four labels in a separate view controller called 'detailViewController' when a user clicks on a table item.
This is the code I have but it's crashing with no obvious error message when the segue is called. Please help!
CupboardViewController
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject!) {
// sender is the tapped `UITableViewCell`
let cell = sender as! ItemTableViewCell
let indexPath = self.tblItems.indexPathForCell(cell)
// load the selected model
let titleToPass = itemMgr.items[indexPath!.row].name
let detail = segue.destinationViewController as! detailViewController
// set the model to be viewed
detail.titleToPass = titleToPass
}
}
detailViewController
#IBOutlet weak var titleDetailLabel: UILabel!
#IBOutlet weak var qtyDetailLabel: UILabel!
#IBOutlet weak var dateDetailLabel: UILabel!
#IBOutlet weak var descriptionDetailLabel: UILabel!
var titleToPass: String!
var qtyToPass: String!
var descriptionToPass: String!
var dateToPass: String!
override func viewDidLoad() {
super.viewDidLoad()
titleDetailLabel.text = titleToPass
qtyDetailLabel.text = qtyToPass
dateDetailLabel.text = dateToPass
descriptionDetailLabel.text = descriptionToPass
}
// Do any additional setup after loading the view.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
ItemTableViewCell
#IBOutlet weak var itemTitle: UILabel!
#IBOutlet weak var itemSubtitle: UILabel!
#IBOutlet weak var itemSubDetail: UILabel!
#IBOutlet weak var dateDetail: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
It is definitely best to avoid the force unwrap ! as much as possible, so I'd definitely recommend using guard let or if let in most cases. In addition to saving you from crashes, they also offer some great ways to see exactly where your code is failing that are otherwise tougher to track down.
For example, you have this line, which returns an optional NSIndexPath
let indexPath = self.tblItems.indexPathForCell(cell)
Rather than force unwrapping this optional, you can modify the line slightly to make it handle nil, and also add a print to tell you that this line failed, like this:
guard let indexPath = self.tblItems.indexPathForCell(cell) else {
print("No index path returned.")
return
}
If the value is not nil you can operate with the indexPath constant like you normally would. You'll probably want to continue this approach with most of these optionals, unless you're 100% sure they won't be nil in any situation.
As for your particular crash, you can correct me if I'm wrong, but I suspect it's because you have those four variables on your detailViewController that are being accessed in viewDidLoad, but you're only setting one of them in the segue. This means that in viewDidLoad you will be trying to access those (and they have the ! too), and since it won't find anything it'll crash immediately. See if fixing that, and getting rid of some of the !s helps.
I have my app with a custom splash screen to load some data from internet. In iOS 8 I can see this ViewController, but in iOS9 I only see it in the first launch of the app the rest of the launches iphone pauses on springboard and then shows the list of data that should be visible after the custom splash screen.
I don't know if there's some bug with ios9 related to this topic :(
Here's is my code, hope anyone can help me.
class SplashScreenVC: UIViewController {
#IBOutlet weak var spinner: UIActivityIndicatorView!
#IBOutlet weak var splashImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "rotated", name: UIDeviceOrientationDidChangeNotification, object: nil)
}
func rotated() {
if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation)) {
splashImageView.image = UIImage(named:"eva_splash_landscape_")
}
if(UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation)) {
splashImageView.image = UIImage(named:"eva_splash_portrait_")
}
}
func startActivityIndicator() {
self.spinner.activityIndicatorViewStyle = .WhiteLarge
self.spinner.center = self.view.center
self.spinner.hidesWhenStopped = true
self.spinner.frame.size.height = 50
self.spinner.frame.size.width = 50
self.parentViewController?.view.addSubview(spinner)
self.spinner.startAnimating()
}
}
Sounds like your Storyboard files could be disconnect from your build target. Make sure Main Interface is your Main.Storyboard file and Launch Screen File is your Custom storyboard file
Here is my viewController to add (View is in a .Xib file)
class EmptyFeedViewController: UIViewController {
#IBOutlet weak var view_center: UIView!
#IBOutlet weak var img_icon: UIImageView!
#IBOutlet weak var lbl_description: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.grayPale()
lbl_description.textColor = UIColor.grayMeduim()
lbl_description.text = "feed_empty".localizeFromFeedTable()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I add it like this from another viewController
let placeholderVC = EmptyFeedViewController()
addChildViewController(placeholderVC)
view_placeholder = placeholderVC.view
On iOS 9 it is working fine but on iOS 8 "lbl_description" is nil
How should I load my view for both OS ?
Since you are using Xibs, in order to get the IBOutlets populated, you should create a new instance of your view controller using the designated initializer - initWithNibName:bundle:
let placeholderVC = EmptyFeedViewController(nibName: "EmptyFeedViewController",
bundle: nil)
I am struggling with this awful project for two weeks now and nothing seems to work. I have to do an app that loads some words from a server and presents them as pins on a map(MKView). I have to cluster the pins when the user zooms-out and for that I have used a this-party library written in Objective-C, but I also had to make a custom callout view with a button. When the user presses the said button the app should go to a TableViewController and here is my problem: I can't make it to do it. I have used before the "performSegueWIthIdentifier" and it worked very well, but now I get the error "there is no segue with '---' identifier". I know there are many other threads with this, but none of the solutions there work for me. Moreover, I have tried to instantiate the ViewController programmatically, but this does not work either because I get the "unexpectedly found nil..." and I don't know what to do any more.
I know I am doing something wrong, most probably how I call these functions, but I don't know what. This is what I have tried until now:
in the .xib file I have this:
import UIKit
class MarkerInfoView: MKAnnotationView {
#IBOutlet weak var theButton: UIButton!
#IBAction func readIt(sender: AnyObject) {
ViewController.goToArticles()
}
}
and in the ViewController:
class func goToArticles(){
ViewController().reallyGoToArticles()
}
I did this because I could not find another way to be able to call performSegueWithIdentifier or presentViewController
func reallyGoToArticles(){
println("let's go!")
let theArticlesSB = UIStoryboard(name: "Main", bundle: nil)
let vc = theArticlesSB.instantiateViewControllerWithIdentifier("theArticles") as! articlesViewController
self.presentViewController(vc, animated: true, completion: nil)
self.performSegueWithIdentifier("showArticles", sender: self)
}
I have uncommented both options just to show you.
I have uploaded the project here
Thank you very much!
Update
I forgot to mention that if I put the line
self.performSegueWithIdentifier("showArticles", sender: self)
in viewDidLoad it works
I had a look at your project and found your issue.
In MarkerInfoView.swift, you call ViewController.goToArticles() which is a class function:
class func goToArticles(){
ViewController().reallyGoToArticles()
}
This class function creates a NEW instance of ViewController which has nothing to do with the storyboard (and is not aware of segues).
You have to call self.reallyGoToArticles() from an instance method like
func goToArticles(){
self.reallyGoToArticles()
}
You have to manage to call the existing ViewController from your MarkerInfoView
EDIT: Here is how to achieve it
MarkerInfoView.swift
class MarkerInfoView: MKAnnotationView {
var vc: ViewController!
#IBOutlet weak var placePhoto: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var detailsLabel: UILabel!
#IBOutlet weak var theButton: UIButton!
#IBAction func readIt(sender: AnyObject) {
vc.goToArticles()
}
}
ViewController.swift
func mapView(mapView: MKMapView!, didSelectAnnotationView annotation: MKAnnotationView!)
{
if let pin = annotation.annotation as? CustomPointAnnotation{
if var infoView = UIView.viewFromNibName("MarkerInfoView") as? MarkerInfoView {
infoView.nameLabel.text = pin.theTitle
infoView.detailsLabel.text = pin.theDetails
infoView.vc = self
infoView.center = CGPointMake(self.view.bounds.width/2, self.view.bounds.height/2)
self.view.addSubview(infoView)
} else {
}
}
}
func goToArticles(){
self.reallyGoToArticles()
}