Swift delegate function not being called from protocol - ios

I am making a library of sorts. One screen, a table view controller, contains a list of books and the other screen, a view controller, allows you to add books.
I'm using the Add Book class to add books to the list of books screen. The add books screen contains text inputs to allow user to enter the book's name, author, publisher etc. When a user clicks a button after entering those fields, it creates a book object containing those attributes, and the user is directed back to the home screen showing the new book added to the list of books. The list of books is a table of rows. Each row contains one book respectively.
I'm using a protocol to call the delegate function in the list of books class, however it's not calling this function. The function not being called is newBook. My code seems fine to me, yet the newBook delegate function is not being called. Please help!. I've added relevant code below:
Add Book class:
import UIKit
protocol AddBookProtocol {
func newBook(book: Book)
}
class AddBookViewController: UIViewController {
var addBookDelegate: AddBookProtocol?
#IBOutlet weak var authorField: UITextField!
#IBOutlet weak var genreField: UITextField!
#IBOutlet weak var editionField: UITextField!
#IBOutlet weak var isbnField: UITextField!
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var publisherField: UITextField!
#IBOutlet weak var descriptionField: UITextField!
#IBAction func saveButton() {
print("HAHHHAHAHAH")
let name = nameField.text
let isbn = isbnField.text
let author = authorField.text
let publisher = publisherField.text
let publishDate = dateField.text
let genre = genreField.text
let edition = editionField.text
let desc = descriptionField.text
let book = Book(title: name!, isbn: isbn!, author: author!, publishDate: publishDate!, genre: genre!, publisher: publisher!, edition: edition!, desc: desc!)
addBookDelegate?.newBook(book: book)
navigationController?.popViewController(animated: true)
}
//rest of the code
Class displaying list of current books:
class CurrentBooksTableViewController: UITableViewController, AddBookProtocol {
var BooksList: [Book] = []
func newBook(book: Book) {
BooksList.append(book)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: BooksList.count - 1, section: 0)], with: .automatic)
tableView.endUpdates()
tableView.reloadSections([SECTION_COUNT], with: .automatic)
}
//rest of the code
}
newBook is not being called for some reason.

Have you set the value of addBookDelegate? You can do this in the prepare for segue function in your table view controller.
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let addBookVC = segue.destination as? AddBookViewController {
addBookVC.addBookDelegate = self
}
}

You need to set the addBookDelegate for AddBookViewController as CurrentBooksTableViewController
Inside CurrentBooksTableViewController before presenting AddBookViewController you need to set addBookDelegate to self.
INSTANCE_OF_ AddBookViewController.addBookDelegate = self
Then only you can access the addBookDelegate functions from other side. otherwise if you check addBookDelegate will be = nil

Related

How can I access data from a container view from the parent view controller in swift?

I have a view controller(SignUpController) and it has 1 image view at the top, 2 buttons at the bottom and 1 view as container view. The Container view(InfoRegisterController) contains scrollview with several text fields. Container view doesn’t contain any buttons. So, now i need to access text fields of Container view in the parent view controller (or can say as : I need to access all data from container view to parent view controller) so that I can register the user by clicking Register button at the bottom in SignUpController. I couldn’t solve it through delegates and NSUserDefaults too. So , Please help me to solve this in swift 4 ?
This is the parent view controller:
import UIKit
import Material
class SignUpController: UIViewController {
#IBOutlet weak var backgroundView: UIView!
#IBOutlet weak var register: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
backgroundView.layer.borderWidth = 1
backgroundView.layer.borderColor = UIColor.blue.cgColor
register.layer.cornerRadius = 5
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // No need for semicolon
let tv: InfoRegisterController = self.childViewControllers[0] as!InfoRegisterController
print("Hello")
//tv.viewWillAppear(true)
}
All data must be accessed to this place so that I can post it to server.
#IBAction func Register(_ sender: Any) {
}
} // class end
Container View:
import UIKit
import Material
class InfoRegisterController: UIViewController, UITextFieldDelegate, UITableViewDelegate,UITableViewDataSource,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
#IBOutlet weak var fullName: TextField!
#IBOutlet weak var Email: ErrorTextField!
#IBOutlet weak var Password: TextField!
#IBOutlet weak var verifyPassword: TextField!
#IBOutlet weak var address: TextField!
#IBOutlet weak var permanentAddress: TextField!
#IBOutlet weak var currentAddress: TextField!
#IBOutlet weak var DOB: TextField!
#IBOutlet weak var occupation: TextField!
#IBOutlet weak var selectGender: TextField!
#IBOutlet weak var identificationType: TextField!
#IBOutlet weak var identificationID: TextField!
#IBOutlet weak var issuedDate: TextField!
#IBOutlet weak var issuedDistrict: TextField!
#IBOutlet weak var licenseNumber: TextField!
#IBOutlet weak var fathersName: TextField!
#IBOutlet weak var grandfathersName: TextField!
#IBOutlet weak var mobileNumber: TextField!
override func viewDidLoad() {
super.viewDidLoad()
self.fullName.delegate = self
self.Email.delegate = self
self.Password.delegate = self
self.verifyPassword.delegate = self
self.address.delegate = self
self.permanentAddress.delegate = self
self.currentAddress.delegate = self
self.DOB.delegate = self
self.occupation.delegate = self
self.identificationID.delegate = self
self.issuedDate.delegate = self
self.issuedDistrict.delegate = self
self.licenseNumber.delegate = self
self.fathersName.delegate = self
self.grandfathersName.delegate = self
self.mobileNumber.delegate = self
}
}
Here, I need to access all fields fullName,Email, Password,verifyPassword,address, permanentAddress,currentAddress, DOB,occupation and all others to SignUpController so that I can register new user.
To gain access to the container view from your parent, you will have to pass a reference from the container view to the parent and then use it in the parent view controller, there are many ways to do that and here is one of them.
In your viewDidAppear of InfoRegisterController which is the container view controller add the following code, this method will get a reference of InfoRegisterController into the parent to be used.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let signUpControllerParent = self.parent as! SignUpController
signUpControllerParent.saveContrainerViewRefference(vc: self)
}
Now in SignUpController add a local variable for the coming reference to be saved and used later to get the data from the textfields.
var infoRegisterRefferenceVC : InfoRegisterController?
Add this method also in your parent SignUpController
func saveContrainerViewRefference(vc:InfoRegisterController){
self.infoRegisterRefferenceVC = vc
}
Now you can have access to all the textfields and the methods in the container view from the parent for example:
var fullNameTextField = self.infoRegisterRefferenceVC.fullName.text
This should be it :)
There are several ways to accomplish this. You can use "Delegate" and "Protocol" or prepareForSegue method in parent view controller.
var somePropertyYouWantToAccess: NSString?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourChildViewSegueIdentifier" {
let childVc = segue.destinationViewController as ContainerViewController
self.somePropertyYouWantToAccess = childVc.firstName
}
}

Swift 3 pass values to Controller on UICollectionViewCell button click

I am beginner in iOS development and I have implemented following screen using UICollectionView :
CollectionViewCell File Code is:
import UIKit
class EventCell: UICollectionViewCell{
var classEvent: Event?
#IBOutlet weak var eventTitle: UILabel!
#IBOutlet weak var eventTeams: UILabel!
#IBOutlet weak var eventTime: UILabel!
#IBOutlet weak var eventTeamOneImage: UIImageView!
#IBOutlet weak var eventTeamTwoImage: UIImageView!
#IBOutlet weak var leaderboardButton: UIButton!
var datasourceItem: Any?{
didSet{
guard let event = datasourceItem as? Event else { return }
classEvent = event
eventTitle.text = "Match \(event.matchNo) (\(event.matchStage))"
eventTeams.text = "\(event.teamOne.nameAttr) vs \(event.teamTwo.nameAttr)"
eventTime.text = "\(event.getEventLockTimeAsString())"
eventTeamOneImage.loadImageUsingCache(withUrl: event.teamOne.flagPhoto)
eventTeamTwoImage.loadImageUsingCache(withUrl: event.teamTwo.flagPhoto)
leaderboardButton.addTarget(self, action: #selector(handleLeaderBoardClick), for: .touchUpInside)
}
}
#IBAction func leagueButton(_ sender: Any) {
}
weak var delegate: HomeControllerDelegate?
func handleLeaderBoardClick() {
if let matchId = classEvent?.id {
print(matchId)
delegate?.clickOnLeaderBoard(matchId: matchId)
}
}
}
Now on click on Leaderboard button(icon with 1,2,3) I would like to open new LeaderBoard Controller and pass matchId which is classEvent.id
How can I pass values to the new controller? And what is the best way to do that.
You can pass the match Id via segue:
In LeaderBoard Controller set a property:
var matchId:Int?
Set a segue between the controller and add an identifier:
On Click leaderboard button:
self.performSegueWithIdentifier("yourSegueIdentifier", sender: self)
Add the segue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue,identifier == "yourSegueIdentifier" {
let destinationVC = segue.destinationViewController as LeaderbardController
destinationVC.matchId = classEvent.id
}
}
}
Three easy steps to get what u want:
Make a BaseViewController class a subclass of UiViewController. This class would be the alternate of UiViewcontroller in your project,it means while creating any viewcontroller BaseViewController will be the parent class.
Declare a variable in BaseViewController.e.g- var data: Any?
Then while moving from a viewcontroller to another , simply assign any type of data to that variable declared in BaseViewController.
And in any lifecycle method of your new viewcontroller you will get that data using self.data.

Inserting rows to tableView using modal popup

I'm making a basic fitness app and within it I have a food diary that tracks the food you eat on a daily basis. I have a modal popup (picture below) that shows when you tap "add food". I'm having trouble inserting rows from the modal popup when the button "Save to Food Diary" is pressed. I'm also trying to update the calories label to the current calories the user accumulated throughout the day. Heres the code i have so far:
class PopupVC: UIViewController {
var section: Int?
var caloriesLabel = " "
var tableData: [String] = [""]
let foodDiary = FoodDiary()
var caloriesCell = caloriesForDiary()
#IBOutlet weak var foodTimeLabel: UILabel!
#IBOutlet weak var foodPopup2: UIView!
#IBOutlet weak var foodPopUp: UIView!
#IBOutlet weak var inputFood: UITextField!
#IBOutlet weak var inputCals: UITextField!
#IBAction func saveToDiary(_ sender: Any) {
if (inputFood.text?.isEmpty)! || (inputCals.text?.isEmpty)! {
return
}
caloriesLabel = foodDiary.testVariable
tableData.append(inputFood.text!)
foodDiary.tableView.beginUpdates()
foodDiary.tableView.insertRows(at: [IndexPath.init(row: (tableData.count) - 1, section: section!)], with: .automatic)
foodDiary.tableView.endUpdates()
dismiss(animated: true, completion: nil)
}
Basically i'm trying to update my FoodDiaryVC using my PopUpVC, and i'm struggling to pass the data between both controllers to insertRows and to update my calories label. Hope this was explained well enough!
The iOS approach would be to create a delegate.
#protocol AddRowDelegate {
func didAddRow(name : String, calories : String)
}
Then your FoodDiaryVC should implement it :
#class FoodDiaryVC : UIViewController, AddRowDelegate {
///Your code
}
Add a delegate variable to your PopUp Class
class PopUpVC : UIVIewController {
weak var delegate : AddRowDelegate?
}
No you have to set the delegate when you show the Popup
myPopUp.delegate = self
When the user tap the "Add to diary", just call the delegate
delegate?.didAddRow(name: "blah", calories : "blah")

Swift: set variable before performing segue

I've a tableView with a cell for each pokemon in the "pokedex". When I press a button in the Cell, I want to show a view with details about this creature.
What I do is that I have a global variable "currentPokemon", that I set to the requested pokemon when the button is pressed. Here's my code for the Cell :
class PokemonTableViewCell: UITableViewCell {
var pokemon: Pokemon!
#IBOutlet weak var pokemonImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var releaseDateLabel: UILabel!
#IBAction func setPokemon(sender: UIButton) {
currentPokemon = self.pokemon
}
}
When I try to access the currentPokemon var in the details view, I get a fatal error because currentPokemon is nil. How could I get this code to be executed before the segue ?
You need to add store for selected Pokemon with singleton instance like:
struct Pokemon {
let name: String
}
class PokemonStore {
static let instance = PokemonStore()
var currectPokemon: Pokemon?
}
Later you can get saved pokemon in any place of your code with PokemonStore.instance.currectPokemon
If you are using segue, you should pass data to detail controller in this UIViewController method:
func performSegue(withIdentifier identifier: String, sender: Any?)

Sending value back from detail to master

I am using a master-detail model in Swift.
However, I want to send a class object created in detail view back to master view. I wrote a unwind function in the master view, but I cannot see the back button in the detail view so I cannot ctrl+drag it to the exit.
Does anyone know how to set the back button to make it visible?
Rather than worrying about hooking up something to the back button, you can update the model directly as the user updates the fields in the detail view controller. To do this you can pass a reference to some model object that contains the properties to be updated (make sure that's a reference type, e.g., a class, and not a struct, though).
For example:
class Person {
var firstName: String?
var lastName: String?
}
class MasterViewController: UIViewController {
#IBOutlet weak var firstNameLabel: UILabel!
#IBOutlet weak var lastNameLabel: UILabel!
var person = Person()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? DetailViewController {
destination.person = person
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
firstNameLabel.text = person.firstName
lastNameLabel.text = person.lastName
}
}
class DetailViewController: UIViewController,UITextFieldDelegate {
var person: Person?
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstNameTextField.text = person?.firstName
lastNameTextField.text = person?.lastName
}
// Note, I specified the detail view controller to be the delegate
// for the two text fields in IB: I then can detect when editing is
// done and act accordingly.
func textFieldDidEndEditing(textField: UITextField) {
switch textField {
case firstNameTextField:
person?.firstName = textField.text
case lastNameTextField:
person?.lastName = textField.text
default:
assert(false, "unidentified textField \(textField)")
}
}
}
You can have master view controller update itself in viewDidAppear, like I did above, or, better, you could add observers for the model properties. But hopefully it illustrates the basic idea.

Resources