Found nil while unwrapping an optional value - Swift 2 - swift2

I am getting this error which I cannot explain:
#IBOutlet weak var licencePlateLabel: UILabel!
var editItem: CarValues? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let editedCar = self.editItem {
if let licencePlate = self.licencePlateLabel {
licencePlate.text? = editedCar.licencePlate!//this gives me nil
}
else {
print("value was nil")
}
print(editedCar.licencePlate!)//this is giving me the correct value
}
if I replace the
if let licencePlate = self.licencePlateLabel {
licencePlate.text! = editedCar.licencePlate!
}//this throws an error "found nil......"
even if I do this I m still getting the "found nil..."
func configureView() {
licencePlateLabel.text = "test"
[...]
}
BUT if I put the above on viewDidLoad then it works fine
override func viewDidLoad() {
licencePlateLabel.text = "test"
[...]
}
What is going on in this code?
EDIT
I am passing the value of the editItem from the detailView to the EditView like this:
#IBAction func editButtonTapped(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let editScreen = storyBoard.instantiateViewControllerWithIdentifier("ID_EditViewController")
self.navigationController?.pushViewController(editScreen, animated: true)
let controller = EditViewController()
controller.editItem = detailItem
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}

You don't unwrap properties to set them, only to read from them. So your code can simply be:
if let licencePlate = self.licencePlateLabel {
licencePlate.text = editedCar.licencePlate
}
Note that because licencePlate.text is an optional value anyway, there is also no need to unwrap editedCar.licencePlate. It's ok to use its value whether it is nil or contains a String.

Related

swift Detect the correct textfield to add an attribute

i´m writing an input text quiz app, and i have an array of Int where i can store if answer was correct or not with 0 or 1 and also i have 3 textfields where i write the answers, and i want to change the textfields back ground color to red or green depending on the answers variable ,, if index variable its 1 change color to green and if is 0 change color to red.. this is what i have
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var textField3: UITextField!
//change int to 1 if answer was correct (3, ea for each textfield)
var answers = [0,0,0]
#IBAction func button(sender: AnyObject) {
for (index, answer) in goodAnswers.enumerate() {
print (answer)
if answer != 0 {
print ("ok")
} else {
print("not ok")
}
}
}
thanks !
You need something like this:
var goodAnswers = ["One", "Two", "Three"]
var textFields:[Int:UITextField]!
override func viewDidLoad() {
super.viewDidLoad()
self.textFields = [0:textField, 1:textField2]
}
#IBAction func btnClick(sender: AnyObject) {
for(index, answer) in goodAnswers.enumerate() {
if textFields[index]?.text == answer {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
UPDATE:
If you want to have another good answers simply change your code to like this:
Declare new property:
var collectionOfGoodAnswers: [Int : [String]]!
and in viewDidLoad() method:
self.collectionOfGoodAnswers = [0: ["Hello", "World"],
1: ["Welcome", "Friend"]]
and then implement the click action:
#IBAction func btnClick(sender: AnyObject) {
for(index, _) in collectionOfGoodAnswers.enumerate() {
guard let goodAnswer = collectionOfGoodAnswers[index] else { return }
guard let answer = textFields[index] else { return }
guard let text = answer.text else { return }
if goodAnswer.contains(text) {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
hope it helps.

How to show route between a MKPointAnnotation and user's current location in swift 2

I am trying to show the route between a MKPointAnnotation and user's current location, but i am fail with it.
My idea is: getting user's current location -> getting the MKPointAnnotation' Coordinate -> line up with MKPolylineRenderer
The problem is that i cannot find the problem. :( I have no idea where i should modify.
class MapInSearch: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var destination: MKMapItem?
var coords: CLLocationCoordinate2D?
let locationManager = CLLocationManager()
var PlaceLat = ""
var PlaceLong = ""// get from previous view controller
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}// step 1
self.mapView.showsUserLocation = true
self.mapView.delegate = self
self.addRoute() // step 2
}
func addRoute() {
var pointsToUse: [CLLocationCoordinate2D] = []
if PlaceLat != "" || PlaceLong != "" {
let coords = "\(PlaceLat), \(PlaceLong)"
let p = CGPointFromString(coords)
pointsToUse += [CLLocationCoordinate2DMake(CLLocationDegrees(p.x), CLLocationDegrees(p.y))]
}
pointsToUse += [CLLocationCoordinate2DMake(CLLocationDegrees(coords!.latitude), CLLocationDegrees(coords!.longitude))]
let myPolyline = MKPolyline(coordinates: &pointsToUse, count: 2)
mapView.addOverlay(myPolyline)
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor.greenColor()
return lineView // step 3
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.coords = manager.location!.coordinate
print("locations = \(coords!.latitude) \(coords!.longitude)")
}
My code is very disorderly because i mixed 4-5 tutorials. Also, these tutorials is written with swift 1.2.(i have tried to edit it to swift 2, but i am fail)
Did you ever resolve your problem? Using the latest iteration of Swift 2 in XCode 7.3, in your view (we will call it MyViewController):
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.mapView.delegate = self
var coordinates : [CLLocationCoordinate2D] = [];
addRoute(coordinates);
}
func addRoute(coordinates: [CLLocationCoordinate2D]) {
// insert your code to populate coordinates array with your coordinates
polyLine = MKPolyline(coordinates: &coordinates, count: coordinates.count)
self.mapView.addOverlay(polyLine, level: MKOverlayLevel.AboveRoads)
}
Then in the same file:
extension MyViewController: MKMapViewDelegate {
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let pr = MKPolylineRenderer(overlay: overlay);
pr.strokeColor = UIColor.blueColor().colorWithAlphaComponent(0.5);
pr.lineWidth = 5;
return pr;
}
}
You may find the important part was the extension. I haven't tested this code, so feel free to correct any issues that crept in.
in your CLLocationManagerDelegate delegate function didUpdateLocations you can update your location by setting
self.myLocation = locations[0] as CLLocation
Then call MakeRoute() - This is a function i wrote to either make a route by car or by walking (hence the self.driveIsSet)
func makeRoute() {
let startPlaceMark = MKPlacemark(coordinate: myLocation.coordinate)
let endPlaceMark = MKPlacemark(coordinate: restLocation.coordinate)
let startMapItem = MKMapItem(placemark: startPlaceMark)
let endMapItem = MKMapItem(placemark: endPlaceMark)
let directionRequest = MKDirectionsRequest()
directionRequest.source = startMapItem
directionRequest.destination = endMapItem
if self.driveIsSet {
directionRequest.transportType = .automobile
} else {
directionRequest.transportType = .walking
}
let directions = MKDirections(request: directionRequest)
directions.calculate { (routeResponse, routeError) in
guard let routeResponse = routeResponse else {
if let routeError = routeError {
print(routeError)
}
return
}
self.mapView.removeOverlays(self.mapView.overlays)
let route = routeResponse.routes[0]
self.mapView.add(route.polyline, level: .aboveRoads)
}
}

Unrecognized selector sent to instance when using UIPanGesture

Hi I am trying to add UIPanGestureRecognizer to UIImageView (in my case, it's an emoji). All other UIGestureRecognizers such as long press, rotation, and pinch work well. However, it gives me an error: unrecognized selector sent to instance when I add UIPanGestureRecognizer. I've spent a day trying to figure out the reason but failed to fix it. Please help! Thanks in advance.
This is a function where I added UIGestureRecognizer to sticker
func emojiInsert(imageName: String) {
deleteButtonHides()
let stickerView: UIImageView = UIImageView(frame: CGRectMake(backgroundImage.frame.width/2 - 50, backgroundImage.frame.height/2 - 50, stickerSize, stickerSize))
stickerView.image = UIImage(named: imageName)
stickerView.userInteractionEnabled = true
stickerView.accessibilityIdentifier = "sticker"
let deleteStickerButton: UIImageView = UIImageView(frame: CGRectMake(stickerView.frame.width - 5 - stickerView.frame.width/3, 5, stickerView.frame.width/3, stickerView.frame.height/3))
deleteStickerButton.image = UIImage(named: "button_back")
deleteStickerButton.accessibilityIdentifier = "delete"
deleteStickerButton.userInteractionEnabled = true
deleteStickerButton.alpha = 0
deleteStickerButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "deleteButtonTouches:"))
stickerView.addSubview(deleteStickerButton)
stickerView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: "handlePinch:"))
stickerView.addGestureRecognizer(UIRotationGestureRecognizer(target: self, action: "handleRotate:"))
stickerView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: "handleLongPress:"))
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
print("emojiInsert : \(imageName)")
backgroundImage.addSubview(stickerView)
}
Below are call back functions I added in the end of the view.swift. I used touchesbegan and touchesMoved to drag an emoji but emoji moved in weird way after rotation. So now I am trying to use UIPanGesture to drag an emoji.
#IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
if(deleteMode) {
return
}
print("handlePinch \(recognizer.scale)")
if let view = recognizer.view {
view.transform = CGAffineTransformScale(view.transform,
recognizer.scale, recognizer.scale)
recognizer.scale = 1
}
}
#IBAction func handleRotate(recognizer : UIRotationGestureRecognizer) {
if(deleteMode) {
return
}
if let view = recognizer.view {
view.transform = CGAffineTransformRotate(view.transform, recognizer.rotation)
recognizer.rotation = 0
}
}
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
if(deleteMode) {
return
}
let translation = recognizer.translationInView(self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
}
#IBAction func handleLongPress(recognizer: UILongPressGestureRecognizer) {
if(recognizer.state == UIGestureRecognizerState.Began) {
if(!deleteMode) {
print("LongPress - Delete Shows")
for (_, stickers) in self.backgroundImage.subviews.enumerate() {
for (_, deleteButtons) in stickers.subviews.enumerate() {
if let delete:UIImageView = deleteButtons as? UIImageView{
if(delete.accessibilityIdentifier == "delete") {
delete.alpha = 1
}
}
}
}
deleteMode = true
} else {
deleteButtonHides()
}
}
}
Again, please help! Thanks in advance.
The problem is that you're missing a colon. In the following line:
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
The handlePan should be handlePan:. That's because the Objective-C signature for your method is:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
The colon is part of the method name.

unexpectedly found nil while unwrapping an Optional value(optional binding)

import UIKit
import Parse
class HomePageViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var homPageTableView: UITableView!
var imageFiles = [PFFile]()
var imageText = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// DO any additional setup after loading the view
var query = PFQuery(className: "Posts")
query.orderByAscending("createdAt")
query.findObjectsInBackgroundWithBlock {
(posts : [AnyObject]?, error : NSError?) -> Void in
if error == nil {
//success fetxhing objects
println(posts?.count)
for post in posts! {
self.imageFiles.append(post["imageFile"] as! PFFile) ---------error here
self.imageText.append(post["imageText"] as! String)
}
println(self.imageFiles.count)
}else{ println(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
As a title, It is keep saying "unexpectedly found nil while unwrapping an Optional value" at the line I draw.
There are a lot of questions about this, it is hard to read code for me, And even if I undertood, mine didn't go right.
where should I use optional binding?
And can u explain it with really easy example what is optional binding?
Thank you
Your post dictionary does not contain an imageFile key. When you access a dictionary using post["imageFile"] the result is the value (if the key exists) and nil (if the key does not exist). You can distinguish between these cases using
if let imageFile = post["imageFile"],
let imageText = post["imageText"] {
self.imageFiles.append(imageFile as! PFFile)
self.imageText.append(imageText as! String)
} else {
print("imageFile and/or imageText missing from \(post)")
}
You need to unwrap your posts before looping through them because posts: [AnyObject]? is optional.
if error == nil {
if let postData = posts{
//success fetxhing objects
println(postData?.count)
for post in postData! {
self.imageFiles.append(post["imageFile"] as! PFFile)
self.imageText.append(post["imageText"] as! String)
}
println(self.imageFiles.count)
}
}else{
println(error)
}

Using xcode 6 and Swift, how can I grab all objectIds from a class on parse and put them into an array?

I am making a tiny quiz app to help my students study for tests. I have the questions on parse.com and successfully can query objects by their ID one at a time, but I end up having to hard code all the objectIds, and what I would like to do is grab the objectIds, put them into an array, and then pull a random question/objectID from that array as the students click through the questions.
I'm a novice, so ... while I may be able to understand the logic, I'm not sure how to write the code.
Here is the code I'm currently using ... but it doesn't include my failed attempts to put the object IDs in an array. I've been trying to add a function CallIDs() with a parse query to get them all, but so far ... no luck. Any ideas?
import UIKit
import Parse
class ViewController: UIViewController {
var ObjectIDs : [String]!
var Question : String!
var Answers : [String]!
var Answer : String!
#IBOutlet var QuestionLabel: UILabel!
#IBOutlet var Button1: UIButton!
#IBOutlet var Button2: UIButton!
#IBOutlet var Button3: UIButton!
#IBOutlet var Button4: UIButton!
#IBOutlet var AnswerResult: UILabel!
#IBOutlet var Next: UIButton!
#IBOutlet var QuizInstructions: UILabel!
var RandomID = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Hide()
CallData()
}
func GetRandomObjectID() {
ObjectIDs = ["jr92lfjbQc","r0C8oC4aJ6","XbTTX8xBRf","cjV2z4PSvV","wATbbu0JoX","9Y6HzfeeoD","mNHCMaao41","5qRcqyyXOL","JaLCoeyA1T","nrnifGOP1T","aDAQ6t3saJ","jKF0ZhmPxh"]
RandomID = Int (arc4random_uniform(UInt32(ObjectIDs.count)))
}
func CallData() {
GetRandomObjectID()
var query : PFQuery = PFQuery(className: "QuestionsandAnswers")
query.getObjectInBackgroundWithId(ObjectIDs[RandomID]) {
(ObjectHolder : PFObject!, error : NSError!) -> Void in
if (error == nil) {
self.Question = ObjectHolder ["Question"] as String!
self.Answers = ObjectHolder ["Answers"] as Array!
self.Answer = ObjectHolder ["Answer"] as String!
if (self.Answers.count > 0) {
self.QuestionLabel.text = self.Question
self.Button1.setTitle(self.Answers[0], forState: UIControlState.Normal)
self.Button2.setTitle(self.Answers[1], forState: UIControlState.Normal)
self.Button3.setTitle(self.Answers[2], forState: UIControlState.Normal)
self.Button4.setTitle(self.Answers[3], forState: UIControlState.Normal)
}
} else {
NSLog("Something is wrong, dude. Sorry.")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func Hide() {
AnswerResult.hidden = true
Next.hidden = true
}
func UnHide() {
AnswerResult.hidden = false
Next.hidden = false
}
#IBAction func Button1Action(sender: AnyObject) {
UnHide()
if (Answer == "0") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button2Action(sender: AnyObject) {
UnHide()
if (Answer == "1") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button3Action(sender: AnyObject) {
UnHide()
if (Answer == "2") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button4Action(sender: AnyObject) {
UnHide()
if (Answer == "3") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Next(sender: AnyObject) {
CallData()
Hide()
}
}
///// well ... here is the function I attempted to code, but it isn't working:
func CallIDs() {
var query = PFQuery(className: “QuestionsandAnswers”) 
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in 
for object in objects {
self.objectIdsArray.append(object.objectId)
  }
}
}
I think the underlying misunderstanding in this question is that objects are to be retrieved using their ids. The requirement is for a random object, and that can be achieved better by knowing only the count of the objects stored on the server.
Getting the count we can select a random object by setting a random skip for a query. So, in the completion block of countObjectsInBackgroundWithBlock, get a random number up to that count like this:
let randomSkip = arc4random_uniform(count)
Now we're ready to do a query with no object id, setting query.skip = randomSkip, and query.limit = 1.
EDIT - If I were to build this in Objective-C, I would do it as follows:
- (void)randomQuestion:(void (^)(NSString *question, NSArray *answers))completion {
PFQuery *countQuery = [PFQuery queryWithClassName:#"QuestionsandAnswers"];
[countQuery countObjectsInBackgroundWithBlock:^(int count, NSError *error) {
NSInteger randomSkip = arc4random_uniform(count);
PFQuery *query = [PFQuery queryWithClassName:#"QuestionsandAnswers"];
query.skip = randomSkip;
query.limit = 1;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if (objects.count) {
PFObject *questionAndAnswerObject = objects[0];
NSString *question = questionAndAnswerObject[#"Question"];
NSArray *answers = questionAndAnswerObject[#"Answers"];
completion(question, answers);
} else {
NSLog(#"no error, but no Q&A objects found");
}
} else {
NSLog(#"there was an error %#", error);
completion(nil, nil);
}
}];
}];
}
I am not sure where you declare the array objectIdsArray. I was unable to find it. However the reason you may be having trouble replacing an existing array is because the function you are calling, findObjectsInBackgroundWithBlock does not run on the main thread. The code will continue to execute rather then waiting for the objectIds to download.
In order to make sure that you have downloaded the objectIds first before you try to use them, you may want to use the following code:
func CallIDs() {
var query = PFQuery(className: “QuestionsandAnswers”)
query.selectKeys(["objectId"])
self.objectIdsArray = query.findObjects()
}
Note: When run, you should see the following message:
Warning: A long-running operation is being executed on the main thread.
However I have not yet found this to be a problem. If the download is quick, it should not be noticeable. If it is taking a considerable amount of time, you can add in a UIActivityIndicatorView.

Resources