Why isn't the data passing between the view controllers? Swift 3 - ios

I have 2 text labels on one view controller and I want to connect the text from the labels to the corresponding UIButtons and UILabels on another View Controller.
Right now when I press on the "thisUploadPhoto" button it loads 2 screens of the next screen, instead of just one screen. If you could help me with that it would be great. Also, when I press the done and cancel buttons on the PhotoLabelViewController it gives me this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[tot.PhotoShareLabelViewController didTapDone:]: unrecognized selector sent to instance 0x7f8cecf5d0b0'
When it is supposed to print 'done'
I don't know why my code below isn't working on connecting the 2 view controllers together.
import UIKit
class PhotoShareViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var thatTextField: UITextField!
#IBOutlet weak var thisTextField: UITextField!
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func thisUploadPhoto(_ sender: Any) {
if thisTextField.text != "" && thatTextField.text != ""
{
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var photoShareLabelViewController = segue.destination as! PhotoShareLabelViewController
photoShareLabelViewController.thisString = thisTextField.text!
photoShareLabelViewController.thatString = thatTextField.text!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
imageView.image = image
}
override var prefersStatusBarHidden: Bool {
return true
}
#IBAction func didTapCancel(_ sender: AnyObject) {
presenter.cancel()
presenter.pop()
}
#IBAction func didTapDone(_ sender: AnyObject) {
guard let message = thatTextField.text, !message.isEmpty else {
return
}
guard let messageOne = thisTextField.text, !messageOne.isEmpty else {
return
}
presenter.finish(with: image, content:message)
presenter.dismiss()
}
}
extension PhotoShareViewController: PhotoShareViewInterface {
var controller: UIViewController? {
return self
}
}
import UIKit
class PhotoShareLabelViewController: UIViewController {
#IBOutlet weak var thisLabel: UILabel!
#IBOutlet weak var thatLabel: UILabel!
#IBOutlet weak var thisButton: UIButton!
#IBOutlet weak var thatButton: UIButton!
var thisCounter = 0
var thatCounter = 0
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func pressedThisButton(_ sender: Any) {
thisCounter += 1
print(thisCounter)
}
#IBAction func pressedThatButton(_ sender: Any) {
thatCounter += 1
print(thatCounter)
}
var thisString = String()
var thatString = String()
#IBAction func pressedButtonDone(_ sender: Any) {
print("done")
}
#IBAction func pressedButtonCancel(_ sender: Any) {
print("cancel")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
thisLabel.text = thisString
thisButton.setTitle(thisString, for: UIControlState.normal)
thatLabel.text = thatString
thatButton.setTitle(thatString, for: UIControlState.normal)
}
}

The "Done" button is wired from Storyboard to call a method called didTapDone in PhotoShareLabelViewController. However, your didTapDone method is implemented in PhotoShareViewController. It's a different view controller.
To fix this, put the method implementation in the correct view controller. Then, select your "Done" button in the Storyboard and go to its "Connections Inspector" [see image below] and remove its current wrong connection. Then, wire it to the correct didTapDone method in the correct view controller.

Related

passing data to a specific label in another view controller, depending on the button pressed

I'm just starting out with swift and decided to create a calorie counting app to test my skills in which I am using an Api to get the nutrition data.
Pressing the add breakfast/lunch/dinner segues to a search view controller from which I pass the calories back.
I am using protocol delegate design pattern. I wanted to know how I could set it up so that when I press the add breakfast button, only the breakfast calorie label is updated and when I press add lunch or dinner, their calorie labels are updated accordingly. any help would be greatly appreciated! I posted the codes of my logViewController and SearchViewController
import UIKit
protocol DataDelegate {
func updateLogCalories(str: String?)
}
class SearchViewController: UIViewController,UITextFieldDelegate,CalorieManagerDelegate{
var delagate: DataDelegate?
#IBOutlet weak var searchTF: UITextField!
#IBOutlet weak var calorieLabel: UILabel!
#IBOutlet weak var foodNameLabel: UILabel!
var calorieManager = CalorieManager()
var logCals : String?
override func viewDidLoad() {
super.viewDidLoad()
calorieManager.delegate=self
searchTF.delegate=self
}
#IBAction func searchPressed(_ sender: Any) {
searchTF.endEditing(true)
print(searchTF.text!)
}
#IBAction func addButtonPressed(_ sender: UIButton) {
delagate?.updateLogCalories(str: logCals)
self.dismiss(animated: true, completion: nil)
}
class LogViewController: UIViewController{
var breakfastCal: String?
#IBOutlet weak var breakfastLabel: UILabel!
#IBOutlet weak var lunchLabel: UILabel!
#IBOutlet weak var totalCaloriesLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navController = segue.destination as! UINavigationController
let destController = navController.topViewController as! SearchViewController
destController.delagate = self
}
#IBAction func addBreakfastPressed(_ sender: UIButton) {
}
#IBAction func addLunchPressed(_ sender: UIButton) {
}
}
extension LogViewController: DataDelegate{
func updateLogCalories(str: String?) {
breakfastLabel.text = str
}
}
If all of your buttons (breakfast, lunch, and dinner) trigger the addButtonPressed action, you need a way to tell which button was pressed, and a way to pass that information to the DataDelegate.
I suggest you put your buttons into an array:
#IBOutlet weak var breakfastButton: UIButton!
#IBOutlet weak var lunchButton: UIButton!
#IBOutlet weak var dinnerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Populate our array of buttons so we can search for a button
buttons = [breakfastButton, lunchButton, dinnerButton]
}
Then modify your DataDelegate protocol to include a meal enum:
enum Meal: Int {
case breakfast = 0
case lunch = 1
case dinner = 2
}
protocol DataDelegate {
func updateLogCalories(str: String?, forMeal meal: Meal)
}
And set up your DataDelegate to implement the new method:
class MyDataDelegate: DataDelegate {
func updateLogCalories(str: String?, forMeal meal: Meal) {
let str = str ?? ""
print("updating calories with string \(str) for meal \(meal)")
}
}
Now modify your addButtonPressed method so it searches the array to figure out which button was pressed.
#IBAction func addButtonPressed(_ sender: UIButton) {
if let index = buttons.firstIndex(of: sender),
let meal = Meal(rawValue: index) {
print("Button at index \(index) pressed")
delegate.updateLogCalories(str: nil, forMeal: meal)
} else {
print("Can't find button or can't create enum.")
}
}

Using segues to pass data in Swift?

I am trying to use segues to pass the data entered in the partyID text field to the partyID label in a separate view controller. However, I am getting errors in my code.
class PartyViewController: UIViewController {
// CALLS LOGIN VC
var LoginViewController: LoginViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var partyID: UITextField!
var token = String()
#IBAction func startParty(_ sender: UIButton) {
self.performSegue(withIdentifier: "partyVCtoGuestPartyVC", sender: self)
performSegue(withIdentifier: "hostStartParty", sender: self)
//LoginViewController?.fetchSpotifyProfile(accessToken )
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "partyVCtoGuestPartyVC"){
let GuestPartyVC = segue.destination as! GuestPartyViewController
GuestPartyVC.partyID = partyID.text
}
And here is the view controller I am trying to pass the data to:
class GuestPartyViewController: UIViewController {
#IBOutlet weak var partyID: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
partyIDLabel.text = partyID
// Do any additional setup after loading the view.
}
I get errors in my override func in LoginVC and then in the partyIDlabel.text in the GuestPartyVC.
In GuestPartyViewController
class GuestPartyViewController: UIViewController {
#IBOutlet weak var partyIDLabel: UILabel!
var partyID: String?
override func viewDidLoad() {
super.viewDidLoad()
partyIDLabel.text = partyID ?? ""
}
}
In PartyViewController
class PartyViewController: UIViewController {
#IBOutlet weak var partyID: UITextField!
#IBAction func startParty(_ sender: UIButton) {
self.performSegue(withIdentifier: "partyVCtoGuestPartyVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "partyVCtoGuestPartyVC",
let GuestPartyVC = segue.destination as? GuestPartyViewController {
GuestPartyVC.partyID = partyID.text
}
}
}
The IBOutlet is weak and may not be instantiate when you pass the text.
Could you try to pass the text in a property, then assign it in the viewDidLoad ?
class GuestPartyViewController: UIViewController {
#IBOutlet weak var partyID: UILabel!
var passedText: String?
override func viewDidLoad() {
super.viewDidLoad()
if let unwrappedPassedText = passedText {
partyIDLabel.text = partyID
}
}
// In the other controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "partyVCtoGuestPartyVC") {
let GuestPartyVC = segue.destination as! GuestPartyViewController
GuestPartyVC.passedText = partyID.text
}
first change the call of login VC
var LoginViewController = LoginViewController()
then put this before the view did load method
#IBOutlet weak var partyID: UITextField!
and can you send the error that you get

Cannot assign value of type 'String?' to type 'UILabel?'

I've just started with programming, and now I try to work with segues and I copy one example: open a new screen with the transfer of text from the text field, then exit this screen to the main one with the transfer of information from the second screen, which is the same text. When I open a finished project, it works great, but mine is not. code is the same, all outlets, actions and segues are the same. And this error I get if I make segues through the screen. Guys, please, help))
FIRST VC
class ViewController: UIViewController {
#IBOutlet weak var loginTF: UITextField!
#IBOutlet weak var passwordTF: UITextField!
#IBOutlet weak var resultLabel: UILabel!
#IBAction func loginTapped(_ sender: UIButton) {
performSegue(withIdentifier: "detailSegue", sender: nil)
}
#IBAction func unwindSegueToMainScreen(segue: UIStoryboardSegue){
guard let svc = segue.source as? SecondViewController else { return }
self.resultLabel = svc.label.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let dvc = segue.destination as? SecondViewController else { return }
dvc.login = loginTF.text
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
SECOND VC
class SecondViewController: UIViewController {
var login: String?
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard let login = self.login else { return }
label.text = "Hello, \(login)"
}
#IBAction func goBackTapped(_ sender: UIButton) {
}
}
Change this
self.resultLabel = svc.label.text
to
self.resultLabel.text = svc.label.text

How to approach putting photos( and labels and buttons) into a Scroll View, having it lengthen every time a new photo is added in Storyboard

Right now on my Storyboard I have a View that I have a segue connecting to a scroll view. I want to make it so that the stuff (an image, label, buttons) on the first View on the Storyboard will go to the Scrollview once the segue button is pressed.
Is there a way that when I segue into the Scroll View the Scroll View will only be the size that it needs to be to fit the newly inputted information from the first View.
Also is there a way that I can save what was put into the Scroll View so the users can add to the scroll view to make it larger. I have Firebase in my app if I need to use that to save the Scroll View information.
Below are my two view controllers (I have no view controller for my scroll view but I can make one if I need it) and a screenshot of my storyboard if that will help!
import UIKit
class PhotoShareViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var thatTextField: UITextField!
#IBOutlet weak var thisTextField: UITextField!
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func thisUploadPhoto(_ sender: Any) {
if thisTextField.text != "" && thatTextField.text != "" {
performSegue(withIdentifier: "segue", sender: nil)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let photoShareLabelViewController = segue.destination as! PhotoShareLabelViewController
photoShareLabelViewController.thisString = thisTextField.text!
photoShareLabelViewController.thatString = thatTextField.text!
photoShareLabelViewController.imageLoaded = image
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
imageView.image = image
}
override var prefersStatusBarHidden: Bool {
return true
}
#IBAction func didTapCancel(_ sender: AnyObject) {
presenter.cancel()
presenter.pop()
}
#IBAction func didTapDone(_ sender: AnyObject) {
guard let message = thatTextField.text, !message.isEmpty else {
return
}
guard let messageOne = thisTextField.text, !messageOne.isEmpty else {
return
}
presenter.finish(with: image, content:message)
presenter.dismiss()
}
}
extension PhotoShareViewController: PhotoShareViewInterface {
var controller: UIViewController? {
return self
}
}
import UIKit
class PhotoShareLabelViewController: UIViewController {
#IBOutlet weak var thisLabel: UILabel!
#IBOutlet weak var thatLabel: UILabel!
#IBOutlet weak var thisButton: UIButton!
#IBOutlet weak var thatButton: UIButton!
#IBOutlet weak var changedImage: UIImageView!
var thisCounter = 0
var thatCounter = 0
#IBAction func pressedDoneButtonLabel(_ sender: Any) {
print("done")
}
var presenter: PhotoShareModuleInterface!
var imageLoaded: UIImage!
#IBAction func pressedThisButton(_ sender: Any) {
thisCounter += 1
print(thisCounter)
}
#IBAction func pressedThatButton(_ sender: Any) {
thatCounter += 1
print(thatCounter)
}
var thisString = String()
var thatString = String()
#IBAction func pressedButtonDone(_ sender: Any) {
print("done")
}
// #IBAction func pressedButtonCancel(_ sender: Any) {
// print("cancel")
// }
override func viewDidLoad() {
super.viewDidLoad()
thisLabel.text = thisString
thisButton.setTitle(thisString, for: UIControlState.normal)
thatLabel.text = thatString
thatButton.setTitle(thatString, for: UIControlState.normal)
changedImage.image = imageLoaded
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Thanks so much! Any and all help is appreciated!
It sounds like what you're trying to do was practically made for a UITableView or UICollectionView. UITableView is actually a subclass of UIScrollView for good reason – the content keeps expanding as needed. You will need to change your data model to be an array of whatever it is you want to display, but that should be fairly easy.
I'm not entirely sure what you're trying to achieve but if I'm correct you have a dynamically changing amount of photos?
If that's the case I would suggest you look at CollectionViews. This will handle the scroll view size for you and help formatting.
Consider these two links
https://developer.apple.com/documentation/uikit/uicollectionview
Great tutorial:
https://www.raywenderlich.com/136159/uicollectionview-tutorial-getting-started

What does this mean? “'NSUnknownKeyException', reason: … this class is not key value coding-compliant for the key X”? [duplicate]

This question already has answers here:
Xcode - How to fix 'NSUnknownKeyException', reason: … this class is not key value coding-compliant for the key X" error?
(79 answers)
Closed 5 years ago.
I am recieving a SIGABRT error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key changeToNewView.'
These are my view controllers:
import UIKit
class PhotoShareViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var thatTextField: UITextField!
#IBOutlet weak var thisTextField: UITextField!
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func thisUploadPhoto(_ sender: Any) {
if thisTextField.text != "" //&& thatTextField.text != ""{
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var photoShareLabelViewController = segue.destination as! PhotoShareLabelViewController
photoShareLabelViewController.thisString = thisTextField.text!
// photoShareLabelViewController.thatString = thatTextField.text!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
imageView.image = image
}
override var prefersStatusBarHidden: Bool {
return true
}
#IBAction func didTapCancel(_ sender: AnyObject) {
presenter.cancel()
presenter.pop()
}
#IBAction func didTapDone(_ sender: AnyObject) {
guard let message = thatTextField.text, !message.isEmpty else {
return
}
guard let messageOne = thisTextField.text, !messageOne.isEmpty else {
return
}
presenter.finish(with: image, content:message)
presenter.dismiss()
}
}
extension PhotoShareViewController: PhotoShareViewInterface {
var controller: UIViewController? {
return self
}
}
PhotoShareLabelViewController:
import UIKit
class PhotoShareLabelViewController: UIViewController {
#IBOutlet weak var thisLabel: UILabel!
#IBOutlet weak var thatLabel: UILabel!
#IBOutlet weak var thisButton: UIButton!
#IBOutlet weak var thatButton: UIButton!
var thisString = String()
// var thatString = String()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
thisLabel.text = thisString
thisButton.setTitle(thisString, for: UIControlState.normal)
// thatLabel.text = thatString
// thatButton.setTitle(thatString, for: UIControlState.normal)
}
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: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I am wondering why this is happening. I checked all of the IBOutlets and IBActions on my story board and they seem to be working fine. The code where I think it's running into the error with is my segue. The code was working fine until I tried to pass data between the two view controllers.
I did the same thing on a completely empty xcode project and the code for passing data seemed to work fine, but when I try to put it in to my actual workspace it seems to have a problem.
Any help with this error would be awesome!
You have a view in your storyboard connected to an outlet in your controller. But you deleted the outlet.
Go to your storyboard, control+click the view and delete the connection by clicking on the x.

Resources