I currently have a UITableView that holds a list of events. Each event has a button where the user can press it to get access to a larger view of a photo associated with the event. I have the UIButton connected and set up but I'm having a hard time setting the image in the new window. Here is my function:
func largeMap(sender: UIButton!) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let manager = NSFileManager.defaultManager()
self.performSegueWithIdentifier("largeMapView", sender: self)
println(sender.tag.description)
println(documentsPath+"/Flight"+(sender.tag.description)+"/Map.png")
if let image = UIImage(data: manager.contentsAtPath(documentsPath+"/Flight"+(sender.tag.description)+"/Map.png")!) {
largeMapView.image = image
}
}
When I run the app, it crashes at largeMapView.image = image and gives me "fatal error: unexpectedly found nil while unwrapping an Optional value". I've checked and the path for the image is correct and it is accessing the image but it's not able to set it to the UIImageView. I have the UIImageView outlet set at the top of the class. Why is this happening? I've set UIImageView's in this manner before but it just won't work now.
Basically the problem is that you are using exclamation marks. Exclamation marks are never safe! They can be a "bad smell", a danger signal - and in your code, that's just what they are.
(1)
I have the UIImageView outlet set at the top of the class.
But you do not show how it is set. You do not show what largeMapView is or how it is declared. The declaration might look like this:
#IBOutlet var largeMapView : UIImageView!
If so, that is bad - that is an implicitly unwrapped optional, which means it might be nil. This could be the source of your problem. By using a conditionally unwrapped optional you are covering up the problem - until you try to set into nil, and then you crash. If it is never being set properly, that's the reason for the crash.
(2)
Along the same same lines, the rest of your code, too, should not force-unwrap Optionals (using ! postfix operator) unless you know for certain that they can never be nil. Rewrite like this so that every Optional is unwrapped conditionally and safely:
if let d = manager.contentsAtPath(documentsPath+"/Flight"+(sender.tag.description)+"/Map.png") {
if let image = UIImage(data: d) {
largeMapView.image = image
}
}
This way, if contentsAtPath fails or UIImage(data:) fails, nothing will happen - you won't set your image - but at least you won't crash. And you can use stepping in the debugger to figure out where you are failing. - But even this will not save you if the problem is that largeMapView is a nil implicitly unwrapped Optional.
Related
I have created a standard outlet for a view that will hold different information based on the button selected on the previous screen.
#IBOutlet weak var labelView: UIView!
It shows it is connected in both the story board view and on the code itself, however, every time I get to any reference to the labelView such as:
if detail.description == "About"
{
labelView.backgroundColor = UIColor.red
}
Then the app crashes out with:
fatal error: unexpectedly found nil while unwrapping an Optional value
I have tried everything I can think of or read on the internet:
Removed and replaced the connection
Deleted the derived data folder like one post suggested
Created a reference to self.view to force it to load
Moved it to viewDidAppear
Moved it to viewWillAppear
Moved it to viewDidLoad (which is where it is currently being
called)
I am sure at this point that the answer is rather simple and I am just completely missing it.
To see where the outlet is being set to nil, try this:
#IBOutlet weak var labelView: UIView? {
didSet {
print("labelView: \(labelView)")
}
}
You should see it set to an initial value when the view is loaded. If it then gets set to nil, put a breakpoint on the print and your should be able to see from the backtrace where it's happening.
Views are lazy initialized. In case you are calling the affected line of code before viewDidLoad() in the views life cycle, try to access viewin advance:
if detail.description == "About" {
_ = self.view
labelView.backgroundColor = UIColor.red
}
I've got a custom UIView of which several instances are created inside a loop:
let models = CDMyModel.MR_findAllSortedBy("position", ascending: true) as! [CDMyModel]
for view in myViews {
view.removeFromSuperview()
}
self.myViews.removeAll(keepCapacity: true)
for model in models {
let myView = MYFaqView(width: CGRectGetWidth(self.view.frame))
myView.titleLabel.text = model.title
myView.content.text = model.content
myView.titleLabel.sizeToFit()
self.scrollView.addSubview(myView)
self.myViews.append(myView)
}
I do sometimes see crashes in Crashlytics in the line with myView.content.text = model.content:
According to the crash I assume it has something to do with memory, but I really don't know how the myView could have been released at that point.
All this happens in viewWillAppear:. Could the removing before has to do something with this? But I assume everything happens on the main thread, so this shouldn't be a problem as well - I'm really stuck here.
The crash happens on iOS 9.
EDIT
MyFaqView init method:
init(width:CGFloat) {
self.width = width
super.init(frame: CGRectZero)
self.addSubview(self.titleLabel)
self.addSubview(self.toggleImageView)
self.addSubview(self.separatorView)
self.content.clipsToBounds = true
self.addSubview(self.content)
self.translatesAutoresizingMaskIntoConstraints = false
self.clipsToBounds = true
}
EDIT
let content:UILabel = {
let l = UILabel()
l.numberOfLines = 0
if let font = UIFont(name: "OpenSans", size: 14) {
l.font = font
}
return l
}()
These problems are always very tricky to track down.
Basically, what is happening is memory corruption. The address 0x14f822a0 that was previously occupied by your UILabel content has been used by something else, in this case a CALayer. You can verify this if the crash happens locally by entering po 0x14f822a0 in lldb and sure enough it will output that address to be of type CALayer.
With these errors, although the crash line can provide a clue, it is not always the cause of the error. Something has already happened elsewhere.
Although Swift is mostly memory managed, there are still pitfalls for the unwary. Personally I have seen two principal causes of memory corruption. The first is with retain cycles caused by self referencing closures. The second - more pertinent to your problem - is with views related to Storyboards and Xibs.
If we follow this through logically, we can consider that the CALayer now occupies the address space previously taken by your UILabel content. The runtime attempts to send a message to the object is thinks occupies that address, and that is caught by a Swift runtime assert which then triggers a EXC_BAD_INSTRUCTION crash.
Now, for some other object to have taken up residence at that address, the original inhabitant the UILabel content must have been released. So why would the runtime release content? Because it is no longer required, i.e. it is not a subview or property of any view that it still required.
I would bet that if you change content to be a subclass UILabel and add a deinit method that you then breakpoint, you will be surprised to see that it is being unexpectedly deinitialised early on. To test this create a type as follows:
class DebugLabel: UILabel
{
func deinit
{
NSLog("Breakpoint on this line here!")
}
}
Then change content's type to be the DebugLabel above.
So why is all this happening? My money is on you having one of your view properties that has been created programmatically as being either weak or unowned. Perhaps you had these set up previously using an IBOutlet that you then removed but forgot to remove the weak designator?
Check through each and every one carefully and I am sure you will find the cause of the problem above. Nothing that is created programatically either by using an initialiser or UINib should be designated weak or unowned.
Brief viewing shows me two potential problems:
You can break iterator here, that cause undefined behavior -
removeFromSuperview() really remove the view from the hierarchy and release it and reduce the number of elements in myViews.
for view in myViews {
view.removeFromSuperview() }
What do you do here? Seems you repeat the previous step.
self.myViews.removeAll(keepCapacity: true)
I know the concept of optionals and forced unwrapping but just to quote an example from iOS 8 Swift Programming Cookbook, I don't understand why var imageView: UIImageView is used
in example 1 but forced unwrapping var imageView: UIImageView! in example 2. Hopefully, someone tells me what I am missing here so I know what to read up on.
Example 1:
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView
required init(coder aDecoder: NSCoder) {
imageView = UIImageView(image: image)
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.center = view.center
view.addSubview(imageView)
}
}
Example 2:
import UIKit
class ViewController: UIViewController {
let image = UIImage(named: "Safari")
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(frame: view.bounds)
imageView.image = image
imageView.center = view.center
view.addSubview(imageView)
}
}
As others have noted, the purpose of these two examples is to illustrate the difference between a non-optional variable that is set during the initialization process and an optional (in this case, an implicitly unwrapped one) which you may instantiate at a later point.
It's worth noting that while this example has pedagogic value, from a practical perspective, when dealing with view controllers, you would use the latter pattern with optional variable set in viewDidLoad (or for programmatically created views, you might use a third pattern, the loadView method), not the first pattern with the non-optional instantiated in init.
I just wanted to make sure you didn't walk away from this example concluding that in the case of view controllers that you should freely use either of these two patterns. There are reasons, unrelated to the Swift language but rather related to details of the life cycle of view controllers, views, etc., that you would favor the optional variable set in viewDidload when dealing with view controllers.
That notwithstanding, it is important to understand in what cases one would use an optional in Swift and in what cases one would not (the key point being that you must use optional if the variable may not be set during the initialization process of the object), so from that perspective, this may be an illuminating example. But just remember, that in the specific case of view controllers, one would generally adopt the viewDidLoad pattern.
In example 1, the variable value is set in init. In example 2, the variable is initialized later and after initialization (until viewDidLoad is called) its value is nil.
Since in example 1 the variable is never nil, it can be a non-optional. However, in example 2 the variable is nil for a long time, so it must be an optional variable.
In swift all non optional class and struct properties must be initialized in a constructor, and there's no way to avoid that (unless it's initialized inline, along with its declaration).
In some cases a non optional property cannot be initialized at instantiation time, because it depends from other properties of the same class, or just it must be initialized at a later stage in the instance lifetime. As such, these properties must be declared as optional.
A workaround consists of declaring all there properties as implicitly unwrapped - internally they are optionals, but they are accessed as non optionals (i.e. without the ? operator).
That explains the differences between example 1 and 2: in the former, the property is initialized in the initializer, so it is declared as non optional. In the latter case instead it is initialized in the viewDidLoad method, and so it's been declared as implicitly unwrapped.
This pattern is widely used in view and view controller outlets - you will notice that when you create an outlet in IB, the corresponding property is an implicitly unwrapped - that's because the variable is assigned after initialization.
Example 1's only initializer is the required init(coder aDecoder: NSCoder), which always assignes a value to imageView. Therefore imageView will always have a value after initilization has taken place.
Example 2 doesn't have or need an initilizer, since both stored properties are given an initial value in their declaration:
let image = UIImage(named: "Safari") // has an initial value of whatever UIImage(named: "Safari") returns
var imageView: UIImageView! // has an initial value of nil
You can therefore construct a ViewController, by calling the initializer which swift adds automatically: ViewController()
This will result in imageView having a value of nil.
class DisplayImageVC: BasePageView { //BasePageView inherits from UIViewController
#IBOutlet weak var displayImage: UIImageView!
override func viewDidLoad() {
self.displayImage.backgroundColor = UIColor.yellowColor()
}
....
in another class I try this and get a fatal error:
var displayImageView = self._pageContent[1] as DisplayImageVC
displayImageView.displayImage.image = UIImage(named: ("displayChecklane"))
Log:
fatal error: unexpectedly found nil while unwrapping an Optional value
Images.xcassets
var displayImageView = self._pageContent[1] as DisplayImageVC
displayImageView.displayImage.image = UIImage(named: ("displayChecklane"))
You are crashing on the second line. You know this is because something is nil, but you do not know what is nil here. You are assuming it is the image. That is a false assumption; if it were correct, there would be no crash, as you are not force-unwrapping it, and it is legal to assign nil to an image view's image.
Instead, consider this: maybe displayImageView.displayImage is nil. That is, what is nil is the UIImageView you are trying to assign to. This would make sense since this is an outlet. If the DisplayImageVC's view has not loaded, its outlets have not been filled - they are still nil.
How you solve this depends on what you want to do. Personally I think your approach is bogus from the start; you have no business setting another view controller's outlet or a property of its outlet, or any aspect of its interface. You should have an image property in DisplayImageVC, and here, you should be setting that property; that way, when the view controller shows its view, it can configure its own interface.
As the question states, I'm having trouble setting the view frame for my custom camera overlay using the Swift language. I keep getting an error that states "value of optional type "CGRect" not unwrapped" that I don't quite understand. The problem line is this one:
cameraOverlay.frame = camera.cameraOverlayView?.frame
Xcode attempts to auto-correct the issue by adding a bang (!) at the end of frame, but that does not work either, and creates another error.
Here is my entire nib initialization code for my custom camera overlay:
#IBOutlet var cameraOverlay: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
var camera = UIImagePickerController()
camera.delegate = self
camera.allowsEditing = false
camera.sourceType = UIImagePickerControllerSourceType.Camera
camera.showsCameraControls = false
NSBundle.mainBundle().loadNibNamed("CameraOverlay", owner: self, options: nil)
cameraOverlay.frame = camera.cameraOverlayView?.frame
camera.cameraOverlayView = cameraOverlay
cameraOverlay = nil
self.presentViewController(camera, animated: false, completion: nil)
}
Any help is appreciated. Thanks!
EDIT
Make sure your nib view does not have a white background, or else the camera will not appear. Set the view background color to clearColor, and make sure opaque is unchecked to be safe. I had this problem for a little while until I realized what was happening.
Use this
cameraOverlay.frame = camera.cameraOverlayView!.frame
Actually you are using ? optional chaining which returns frame as wrapped in optional.! is used for optional unwrapping
or you can also do
//it will not crash but you should handle nil case using unwrap by `!` as shown above using `if` condition
cameraOverlay.frame = (camera.cameraOverlayView?.frame)!