iOS How do I add a new item to a collection view - ios

So I have a CollectionView as my home view, then I have a second view as a table view controller that adds a single image to itself. As soon as I press the “save” button on the top right I get the error code: See image of what I'm trying to acheive
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
I have a model of array items that take in only images. How can I pass my uploaded image to my CollectionView from my Table View and update the CollectionView with the uploaded item
This is where the error occurs:
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
if let uploadedImage = uploadImageView.image {
items.image = uploadedImage
}
dismiss(animated: true, completion: nil)
}

You get this issue when you're force unwrapping an optional and it's actually empty. Try:
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
if let uploadedImage = uploadImageView?.image {
items.image = uploadedImage
} else {
print("uploadImageView is nil check the #IBOutlet connection for uploadImageView!!!")
}
dismiss(animated: true, completion: nil)
}

for this problem i suggest you to use guard to made sure your optional variable is not nil
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
guard let uploadedImage = uploadImageView.image else {
//do something if nill here
return
}
items.image = uploadedImage
dismiss(animated: true, completion: nil)
}

Related

Unexpectedly found nil while unwrapping an Optional value / Swift

Building the ToDo app. The app crashes when the new todo task is created.
The breakpoint stops the code and returns:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
#IBAction func doneButton(_ sender: UIButton) {
guard let title = textView.text, !title.isEmpty else {
return
}
let todo = Todo(context: managedContext)
todo.title = title
todo.priority = Int16(segmentedControl.selectedSegmentIndex)
todo.date = Date()
do {
try managedContext.save()
dismiss(animated: true)
textView.resignFirstResponder()
} catch {
print("Error saving todo: \(error)")
}
}
#IBAction func cancelButton(_ sender: UIButton) {
dismiss(animated: true)
textView.resignFirstResponder()
}
Any ideas what could have caused the app crash? Thanks
UISegmentedControlSegment is the public enum and UISegmentedControl is the UIControl
As per your comment, it seems that you have mistaken UISegmentedControl for UISegmentedControlSegment, so connect UISegmentedControl like below:
#IBOutlet weak var segmentedControl: UISegmentedControl!

Open different new view controllers by clicking different elements in table view cell - Swift 3

My table view cell displays an entity with two different button elements. I want to be able to launch a view controller that displays a selection of food items if I click on the first button and a different view controller that displays a selection of beverages when I click on the second button.
I am able to correctly pass the data to the new view controllers, but can't seem to dismiss the current view and load the new one. My code is like this:
In the table view cell
#IBAction func foodBtnPressed(_ sender: Any) {
print("foodBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Food", forCustomer: customer!)
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
print("beverageBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Beverage", forCustomer: customer!)
}
In the table view controller
func loadChooserScreen(toChoose: String, forCustomer: Customer) {
print("Choose \(toChoose)")
print("For \(forCustomer.name)")
if toChoose == "Food" {
let foodVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "foodMenu") as? FoodVC
foodVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else if toChoose == "Beverage" {
let beverageVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "beverageMenu") as? BeverageVC
beverageVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else {
// do nothing
}
}
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
dismiss(animated: true, completion: {
self.delegate!.dismissViewController()
})
}
In this view controller I also have the following protocol
protocol OrderVCProtocol {
func dismissViewController()
}
and have defined
var delegate: OrderVCProtocol!
In my root view controller
func dismissViewController() {
print("Reached dismissViewController function in rootView")
if let foodVC = self.storyboard?.instantiateViewController(withIdentifier: "foodMenu") {
self.present(foodVC, animated: true, completion: nil)
}
if let beverageVC = self.storyboard?.instantiateViewController(withIdentifier: "beverageMenu") {
self.present(beverageVC, animated: true, completion: nil)
}
}
And the delegate is set when the table view controller is called here
#IBAction func loadOrderView(_ sender: Any) {
let orderVC = self.storyboard?.instantiateViewController(withIdentifier: "orderView") as! OrderVC
orderVC.delegate = self
self.present(orderVC, animated: true, completion: nil)
}
Within my target view controllers I have the following function
func loadCustomerToEdit(customer: Customer) {
self.customerToEdit = customer
print("IN FoodVC THE CUSTOMER TO EDIT IS \(self.customerToEdit.name)")
}
and a corresponding one in the BeverageVC.
When I run the app, no errors are thrown and I get the following sample output in the console from my print statements:
foodBtn pressed
customer is Optional("John")
Choose Food
For Optional("John")
IN FoodVC THE CUSTOMER TO EDIT IS Optional("John")
Reached dismissVC function in selectionMenu
and a corresponding response if the beverage button is clicked.
Then nothing happens. So I know the data is correctly being passed to the new view controllers but I don't know how to dismiss the current screen and display the new one with the choices.
I hope my question is clear enough? I'm not sure what's wrong, but the console output clearly shows that the code runs fine until it tries to dismiss the current view.
EDITED TO ADD:
If I modify my dismissVC function in my tableview controller like this:
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
delegate.dismissViewController()
}
the console view now throws
fatal error: unexpectedly found nil while unwrapping an Optional value
And if I modify it again to the following, It goes back to throwing no errors and getting stuck at the same place (i.e. printing the line "Stuck where delegate dismisses view"), showing that the delegate is still nil... but why is it nil when I'd set it in the root view and loaded it in this view?
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
if delegate != nil {
delegate?.dismissViewController()
} else {
print("Stuck where delegate dismisses view")
}
I have solved my problem by implementing notifications via notification centre and delegates. Firstly, in my AppDelegate file I added this line at the bottom
let notifyCnt = NotificationCenter.default
Next, I modified my tableview cell functions to this
#IBAction func foodBtnPressed(_ sender: Any) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Food", "forCustomer": customer])
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Beverage", "forCustomer": customer])
}
Then, in the tableview controller I modified it to this:
protocol ChooserViewDelegate: class {
func loadChooserView(choice: String, forCustomer: Customer)
}
and defined
weak var delegate: ChooserViewDelegate?
and added this within my ViewDidLoad section
notifyCnt.addObserver(forName: Notification.Name(rawValue: "toChoose"), object: nil, queue: nil, using: loadChooserScreen)
and finally modified my chooser function like so:
func loadChooserScreen(notification: Notification) {
guard let userInfo = notification.userInfo,
let toChoose = userInfo["toChoose"] as? String,
let planToEdit = userInfo["customer"] as? Customer else {
print("No userInfo found in notification")
return
}
delegate?.loadChooserView(choice: toChoose, forCustomer: customer)
}
Then in my root view controller I have the following to replace what I had earlier:
/*Conform to ChooserViewDelegate Protocol */
func loadChooserView(choice: String, forCustomer: Customer) {
self.customer = forCustomer
dismiss(animated: false, completion: {
if choice == "Food" {
self.performSegue(withIdentifier: "food", sender: self.customer)
}
if choice == "Beverage" {
self.performSegue(withIdentifier: "beverage", sender: self.customer)
}
})
}
and I send over the data via prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "food" {
if let foodVC = segue.destination as? FoodVC {
storyboard?.instantiateViewController(withIdentifier: "food")
foodVC.customerToEdit = self.customerToEdit
foodVC.delegate = self
}
}
if segue.identifier == "beverage" {
if let beverageVC = segue.destination as? BeverageVC {
storyboard?.instantiateViewController(withIdentifier: "beverage")
beverageVC.customerToEdit = self.customerToEdit
beverageVC.delegate = self
}
}
}
So now everything loads and views correctly :)

Can't connect custom IBAction to button

I'm following a ResearchKit tutorial in swift (http://www.raywenderlich.com/104575/researchkit-tutorial-with-swift) and I can't connect either of my IBaction methods to buttons in the main storyboard. Here's the code (from the tutorial):
import ResearchKit
class ViewController: UIViewController {
#IBAction func consentTapped(sender : AnyObject) {
let taskViewController = ORKTaskViewController(task: ConsentTask, taskRunUUID: nil)
taskViewController.delegate = self
presentViewController(taskViewController, animated: true, completion: nil)
}
#IBAction func surveyTapped(sender : AnyObject) {
let taskViewController = ORKTaskViewController(task: SurveyTask, taskRunUUID: nil)
taskViewController.delegate = self
presentViewController(taskViewController, animated: true, completion: nil)
}
}
extension ViewController : ORKTaskViewControllerDelegate {
func taskViewController(taskViewController: ORKTaskViewController, didFinishWithReason reason: ORKTaskViewControllerFinishReason, error: NSError?) {
taskViewController.dismissViewControllerAnimated(true, completion: nil)
}
}
I go into the main storyboard and click on the View Controller, and I've tried setting my class both to ViewController and UIViewController, and the methods are supposed to show up in Sent Events, but they don't. I've also tried ctrl dragging, and that hasn't worked for me either.
The issue was that I was not ctrl-dragging from my button directly to the yellow view controller button while the view controller class was set to the class where my IBaction methods were.
You can change the #IBAction's argument from (_ sender: Any) to (_ sender: AnyObject), but manually.
And try to use Ctrl+Drag, please.
It might work.

Swift segue fatal error

In ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
svc.vericik = self.vericik
}
}
#IBAction func gotoView2(sender: AnyObject) {
self.performSegueWithIdentifier("Segue", sender: self)
self.presentViewController(ViewController2(), animated: true, completion: nil)
}
In ViewController2:
var vericik: String!
#IBOutlet weak var VeriYeri: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
VeriYeri.text = vericik
}
When, I click button on ViewController, ViewController2 page comes to screen and I can see segue data which come from ViewController. But after that, an error occurs:
fatal error: unexpectedly found nil while unwrapping an Optional value
Where am I doing wrong?
Your problem is this line:
self.presentViewController(ViewController2(), animated: true, completion: nil)
This line of code is creating a second ViewController2. The first one was created for you when you did self.performSegueWithIdentifier("Segue", sender: self). This second ViewController2 never gets initialized, so its vericik property is still nil when viewDidLoad runs and implicitly unwraps the optional with VeriYeri.text = vericik (because vericik is declared as String!).
To fix the problem, simply delete this line of code:
self.presentViewController(ViewController2(), animated: true, completion: nil)
It is not needed. The segue creates ViewController2 for you, and you initialize it in prepareForSegue, and then the segue presents ViewController2. There's absolutely no need for you to call presentViewController when using segues.
Somehow, you are setting your vericik variable to nil. Can you print that value before you try setting it before the segue? If it's nil, don't set it as the text, because that's causing the crash.
You need to check that your string is not nil. Try this.
ViewController2
override func viewDidLoad() {
super.viewDidLoad()
if vericik != nil {
//String is not nil, set textfield/label whatever
VeriYeri.text = vericik
}
}
ViewController
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
if self.vericik != nil {
//String is not nil. All is good :)
svc.vericik = self.vericik
} else {
//String is nil, do something...maybe set default text
svc.vericik = "some text"
}
}

Protocols and Delegates in Swift

I have two View Controllers: "DiscoverViewController" and "LocationRequestModalViewController".
The first time a user opens the "DiscoverViewController", I overlay "LocationRequestModalViewController" which contains a little blurb about accessing the users location data and how it can help them.
On the "LocationRequestModalViewController" there are two buttons: "No thanks" and "Use location". I need to send the response from the user back to the "DiscoverViewController"
I have done some research and found that delegates/protocols are the best way to do it, so I followed a guide to get that working, but I'm left with 2 errors and can't figure them out.
The errors are:
On DiscoverViewController
'DiscoverViewController' is not convertible to 'LocationRequestModalViewController'
On LocationRequestModalViewController
'LocationRequestModalViewController' does not have a member name 'sendBackUserLocationDataChoice'
I've marked where the errors are happen in the following files:
DiscoverViewController.swift
class DiscoverViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate, LocationRequestModalViewControllerDelegate {
func showLocationRequestModal() {
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var locationRequestVC: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("locationRequestVC")
self.presentingViewController?.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.tabBarController?.presentViewController(locationRequestVC as UIViewController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
}
func sendBackUserLocationDataChoice(controller: LocationRequestModalViewController, useData: Bool) {
var enableData = useData
controller.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
showLocationRequestModal()
}
}
LocationRequestModalViewController
protocol LocationRequestModalViewControllerDelegate {
func sendBackUserLocationDataChoice(controller:LocationRequestModalViewController,useData:Bool)
}
class LocationRequestModalViewController: UIViewController {
var delegate:LocationRequestModalViewController? = nil
#IBAction func dontUseLocationData(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func useLocationData(sender: AnyObject) {
delegate?.sendBackUserLocationDataChoice(self, useData: true) // This is where error #2 happens
}
override func viewDidLoad() {
super.viewDidLoad()
//Modal appearance stuff here...
}
}
The answer is in your question itself. Both errors tells the exact reason.
Issue 1
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
The self is of type DiscoverViewController
But you declared the delegate as:
var delegate:LocationRequestModalViewController? = nil
You need to change that to:
var delegate:DiscoverViewController? = nil
Issue 2
The same reason, LocationRequestModalViewController does not confirm to the LocationRequestModalViewControllerDelegate, change the delegate declaration.
You have defined your delegate as having type LocationRequestModalViewController which does not conform to LocationRequestModalViewControllerDelegate.

Resources