How can I modify the tableView in the second VC without affecting the related first VC? - ios

the problem is that I have currency flags and currency codes which are used in both of my screens. one is the first screen showing selected currency and the related flag and in the other screen i have same flags and codes next to each other inside tableview. Whichever i click on tableview i get it in the first screen as selected flag and code. what i want is I want to write a description next to the code like "{emoji} - USD - American Dollars" in the tableview but when i click it i dont want to see the American Dollars explanation in the first screen. how can i do that? this is what I want as a result and below that I m adding my codes
The Result I want
First VC
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var amountText: UITextField!
#IBOutlet weak var amountText2: UITextField!
#IBOutlet weak var fromLabel: UILabel!
#IBOutlet weak var fromImage: UIImageView!
#IBOutlet weak var toImage: UIImageView!
#IBOutlet weak var toLabel: UILabel!
var currencyManager = CurrencyManager()
var from: String = "EUR"
var to: String = "TRY"
var amount: String = "0"
override func viewDidLoad() {
super.viewDidLoad()
amountText.delegate = self
currencyManager.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let currencySelectorViewController = segue.destination as? CurrencySelectorViewController {
if let sender = sender as? Int {
if sender == 1 {
currencySelectorViewController.fromCurrencySelection = self
} else {
currencySelectorViewController.toCurrencySelection = self
}
}
}
}
#IBAction func amountChanged(_ sender: UITextField) {
amount = sender.text!
//amountText.endEditing(true)
currencyManager.fetchRates(from: from, to: to, amount: amount )
}
//#IBAction func editingChanged2(_ sender: UITextField) {
// amount = sender.text!
//
// //amountText.endEditing(true)
// currencyManager.fetchRates(from: to, to: from, amount: amount )
// }
#IBAction func didTapView(_ sender: UITapGestureRecognizer) {
performSegue(withIdentifier: "currencySelector", sender: 1)
}
#IBAction func didTapView2(_ sender: UITapGestureRecognizer) {
performSegue(withIdentifier: "currencySelector", sender: 2)
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
amountText.endEditing(true)
currencyManager.fetchRates(from: from, to: to, amount: amount )
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
amountText.placeholder = "Enter an amount"
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
//amount = amountText.text!
//amount = amountText2.text!
amountText.text = ""
//amountText2.text = ""
}
}
extension ViewController: CurrencyManagerDelegate {
func didFailWithError(error: Error) {
print(error)
}
func didUpdateCurrency(_ currencyManager: String) {
DispatchQueue.main.async {
self.amountText2.text = currencyManager
}
}
}
extension ViewController: FromCurrencySelectorDelegate, ToCurrencySelectorDelegate {
func didGetCurrencyCode(from: String) {
fromLabel.text = from
self.from = from
}
func didGetCurrencyCode(to: String) {
toLabel.text = to
self.to = to
}
}
Second VC
import UIKit
protocol FromCurrencySelectorDelegate: AnyObject {
func didGetCurrencyCode(from: String)
}
protocol ToCurrencySelectorDelegate: AnyObject {
func didGetCurrencyCode(to: String)
}
class CurrencySelectorViewController: UIViewController {
weak var fromCurrencySelection: FromCurrencySelectorDelegate!
weak var toCurrencySelection: ToCurrencySelectorDelegate!
let reusableCell = "ReusableCell"
let currencyArray = ["🇪🇺 EUR", "\u{1F1FA}\u{1F1F8} USD", "\u{1F1F9}\u{1F1F7} TRY", "\u{1F1EC}\u{1F1E7} GBP", "\u{1F1EF}\u{1F1F5} JPY", "\u{1F1E8}\u{1F1E6} CAD", "\u{1F1E6}\u{1F1FA} AUD", "\u{1F1E7}\u{1F1EC} BGN", "\u{1F1F7}\u{1F1FA} RUB", "\u{1F1F3}\u{1F1F4} NOK", "\u{1F1E8}\u{1F1F3} CNY", "\u{1F1E8}\u{1F1ED} CHF", "\u{1F1F2}\u{1F1FD} MXN"]
var filteredData: [String]!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
filteredData = currencyArray
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
}
}
extension CurrencySelectorViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reusableCell, for: indexPath)
cell.textLabel?.text = filteredData[indexPath.row]
return cell
}
}
extension CurrencySelectorViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if fromCurrencySelection != nil {
fromCurrencySelection.didGetCurrencyCode(from: currencyArray[indexPath.row])
} else {
toCurrencySelection.didGetCurrencyCode(to: currencyArray[indexPath.row])
}
dismiss(animated: true, completion: nil)
}
}
//MARK: - Search Bar Methods
extension CurrencySelectorViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = []
if searchText == "" {
filteredData = currencyArray
}
for word in currencyArray {
if word.uppercased().contains(searchText.uppercased()) {
filteredData.append(word)
}
}
self.tableView.reloadData()
}
}

Related

How to add search functionality to table view which has yelp api data?

I want to add restaurant category search functionality to RestaurantTableViewController which use yelp api business data. I followed basic search bar in table view tutorials but I do not do for my specific scenario. I do not differ filtered data and not filtered data in my RestaurantTableViewController when the search is active.
RestaurantTableViewController is below:
import UIKit
import CoreLocation
protocol ListActions: class {
func didTapCell(_ viewController: UIViewController, viewModel: RestaurantListViewModel)
}
class RestaurantTableViewController: UIViewController, UITableViewDelegate, FiltersViewControllerDelegate {
#IBOutlet weak var yourLocationLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
private let locationManager = CLLocationManager()
private let locationService = LocationService()
var filteredData: [RestaurantListViewModel]!
let appDelegate = UIApplication.shared.delegate as? AppDelegate
var viewModels = [RestaurantListViewModel]() {
didSet {
DispatchQueue.main.async {
// this no more loading, i notice it load late that is why when reload data in table view not working
}
}
}
weak var delegate: ListActions?
override func viewDidLoad() {
super.viewDidLoad()
filteredData = appDelegate!.data
userCurrentLocation()
DispatchQueue.main.async {
self.tabBarController?.tabBar.isHidden = false
}
if appDelegate?.data?.count == 0 {
tableView.setEmptyView(title: "There are no restaurants in your current location.", message: "Please change your location and try again!")
}
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
if appDelegate?.data?.count == 0 {
tableView.setEmptyView(title: "There are no restaurants in your current location.", message: "Please change your location and try again!")
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("did appear")
self.removeActivityIndicator()
tableView.reloadData()
tableView.dataSource = self
tableView.delegate = self
DispatchQueue.main.async {
self.tabBarController?.tabBar.isHidden = false
}
}
}
extension RestaurantTableViewController: UITableViewDataSource {
// MARK: - Table view data source
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "RestaurantCell"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("number of rows \(String(describing: viewModels.count))")
print(viewModels)
if appDelegate?.data?.count == 0 {
self.tableView.setEmptyView(title: "There are no restaurants in your current location.", message: "Please change your location and try again!")
}
if (self.searchBar.isUserInteractionEnabled) {
return self.filteredData.count
}
else {
return appDelegate!.data?.count ?? 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RestaurantCell", for: indexPath) as! RestaurantTableViewCell
if (self.searchBar.isUserInteractionEnabled) {
let vm = filteredData?[indexPath.row]
cell.configure(with: vm!)
return cell
} else {
let vm = appDelegate!.data?[indexPath.row]
cell.configure(with: vm!)
return cell
}
}
// MARK: - Delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let detailsViewController = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController")
else {return}
navigationController?.pushViewController(detailsViewController, animated: true)
let vm = appDelegate!.data?[indexPath.row]
appDelegate!.didTapCell(detailsViewController, viewModel: vm!)
if let selectedRowIndexPath = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: selectedRowIndexPath, animated: true)
}
}
}
extension RestaurantTableViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = searchText.isEmpty ? appDelegate?.data : appDelegate?.data?.filter { (item: RestaurantListViewModel) -> Bool in
return item.categories[0].title.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil
}
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
}
}
My RestaurantTableViewCell data is below and I am getting restaurant table view cell data in configure function. But in RestaurantTableViewController's cellForRow method I do not differ my filtered and normal data when search is active.
class RestaurantTableViewCell: UITableViewCell {
#IBOutlet weak var cardContainerView: ShadowView!
#IBOutlet weak var restaurantImageView: UIImageView!
#IBOutlet weak var makerImageView: UIImageView!
#IBOutlet weak var restaurantNameLabel: UILabel!
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var restaurantType: UILabel!
let cornerRadius : CGFloat = 10.0
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
cardContainerView.layer.cornerRadius = cornerRadius
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
cardContainerView.layer.shadowColor = UIColor.gray.cgColor
cardContainerView.layer.shadowOffset = CGSize(width: 5.0, height: 5.0)
cardContainerView.layer.shadowRadius = 15.0
cardContainerView.layer.shadowOpacity = 0.9
restaurantImageView.layer.cornerRadius = cornerRadius
restaurantImageView.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configure(with viewModel: RestaurantListViewModel) {
// For background thread
DispatchQueue.global(qos: .background).async {
DispatchQueue.main.async {
self.restaurantImageView.af_setImage(withURL: viewModel.imageUrl)
self.restaurantNameLabel.text = viewModel.name
self.locationLabel.text = "\(viewModel.formattedDistance) m"
}
}
if let restaurantType: String = String(viewModel.categories[0].title) {
self.restaurantType.text = restaurantType
}
}
}
RestaurantListViewModel is below also:
struct Business: Codable {
let id: String
let name: String
let imageUrl: URL
let distance: Double
let categories: [Category]
}
struct RestaurantListViewModel {
let name: String
let imageUrl: URL
let distance: Double
let id: String
let categories: [Category]
}
extension RestaurantListViewModel {
init(business: Business) {
self.name = business.name
self.id = business.id
self.imageUrl = business.imageUrl
self.distance = business.distance
self.categories = business.categories
}
}

How I could save data using realm in tableview?

I have table where I can add some empty cells. Using protocol I can send some data to each cell. When I am looking into the table I can see all the information I sent. My problem happens when try to save date using realm, only save last sent data to cell.
// model:
import UIKit
import RealmSwift
class HITActionModel: Object {
#objc dynamic var seconds: Int = 0
#objc dynamic var color: String = ""
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
var parentWorkout = LinkingObjects(fromType: WorkoutModel.self, property: "actionsArray")
}
class WorkoutModel: Object {
#objc dynamic var title: String?
#objc dynamic var rounds: Int = 0
var actionsArray = List<HITActionModel>()
}
//protocol
protocol HITActionCellDelegate {
func action(sec: Int, name: String, id: Int)
}
//custom cell
class HITActionCell: UITableViewCell {
//MARK: - Properties
#IBOutlet weak var changeColorBtn: UIButton!
#IBOutlet weak var actionLabel: UILabel!
#IBOutlet weak var actionNameTextField: UITextField!
var delegate: HITActionCellDelegate?
var numberOfSeconds = 0
var id = 0
//MARK: - Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
}
//MARK: - Elements setup
func setupElements() {
actionNameTextField.placeholder = "Add name for Action!"
changeColorBtn.backgroundColor = UIColor.neonYellow
actionLabel.text = "Entry seconds for action"
}
#IBAction func addActionSeconds(_ sender: Any) {
numberOfSeconds += 1
actionLabel.text = "\(numberOfSeconds) SECONDS"
delegate?.action(sec: numberOfSeconds, name: actionNameTextField.text ?? "", id: self.id)
}
#IBAction func reduceActionSeconds(_ sender: Any) {
if numberOfSeconds > 0 {
numberOfSeconds -= 1
actionLabel.text = "\(numberOfSeconds) SECONDS"
delegate?.action(sec: numberOfSeconds, name: actionNameTextField.text ?? "", id: self.id)
}
}
}
// From here I send data using Protocol to viewController :
import UIKit
import RealmSwift
class AddActionViewController: UIViewController {
//MARK: - Properties
let workout = WorkoutModel()
let action = HITActionModel()
let realm = try! Realm()
var numberOfRounds = 0
#IBOutlet weak var workoutNameTextField: UITextField!
#IBOutlet weak var workoutNameLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var roundsLabel: UILabel!
#IBOutlet weak var addRounds: UIButton!
#IBOutlet weak var reduceRounds: UIButton!
#IBOutlet weak var saveButton: UIButton!
#IBOutlet weak var cancelButton: UIButton!
#IBOutlet weak var addActionButton: UIButton!
//MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
print(Realm.Configuration.defaultConfiguration.fileURL)
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(UINib(nibName: Const.UI.NibName.hiActionCell, bundle: nil), forCellReuseIdentifier: Const.UI.CellIdentifier.hiActionCell)
}
//MARK:- Elements setup
func setupUI() {
view.backgroundColor = UIColor.black
title = Const.NameString.workouts
addActionButton.setTitle(Const.NameString.addAction, for: .normal)
addActionButton.tintColor = UIColor.black
addActionButton.titleLabel?.font = UIFont.textStyle9
workoutNameLabel.text = Const.NameString.workoutName
workoutNameLabel.font = UIFont.textStyle7
workoutNameLabel.textColor = UIColor.white
workoutNameTextField.textColor = UIColor.black
workoutNameTextField.font = UIFont.textStyle8
roundsLabel.text = Const.NameString.startingRounds
roundsLabel.font = UIFont.textStyle9
roundsLabel.textColor = UIColor.black
roundsLabel.textAlignment = .center
}
#IBAction func saveButtonAction(_ sender: Any) {
if workoutNameTextField.text != "" {
workout.title = workoutNameTextField.text!
workout.rounds = numberOfRounds
save(workout)
self.dismiss(animated: true, completion: nil)
} else {
let myalert = UIAlertController(title: "Message", message: "You forgot something to add ;) (name, rounds, seconds)", preferredStyle: UIAlertController.Style.alert)
myalert.addAction(UIAlertAction.init(title: "I'm guilty", style: .default, handler: nil))
self.present(myalert, animated: true)
}
}
func save(_ workout: WorkoutModel) {
do {
try realm.write {
realm.add(workout)
}
} catch {
print(error.localizedDescription)
}
}
#IBAction func cancelButtonAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func addActionButtonPressed(_ sender: Any) {
workout.actionsArray.append(action)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: workout.actionsArray.count - 1, section: 0)], with: .fade)
tableView.endUpdates()
}
#IBAction func addRoundsBtn(_ sender: Any) {
numberOfRounds += 1
roundsLabel.text = "\(numberOfRounds) ROUNDS"
}
#IBAction func reduceRoundsBtn(_ sender: Any) {
if numberOfRounds > 0 {
numberOfRounds -= 1
roundsLabel.text = "\(numberOfRounds) ROUNDS"
}
}
}
//MARK:- Setup TextFieldDelegate Method
extension AddActionViewController: UITextFieldDelegate {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
workoutNameTextField.resignFirstResponder()
return true
}
}
//Quick guide implementation for showing and deleting cells
extension AddActionViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workout.actionsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: Const.UI.CellIdentifier.hiActionCell) as? HITActionCell else {
return UITableViewCell()
}
cell.delegate = self
cell.setupElements()
cell.id = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
workout.actionsArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
if (editingStyle == .insert) {
print("test")
}
}
extension AddActionViewController: HITActionCellDelegate {
func action(sec: Int, name: String, id: Int) {
action.name = name
action.seconds = sec
action.id = id
}
}
If could someone help me, It would be greet for me :)

Why is nothing being sent to my tableview?

I am creating a news feed, but nothing is being sent to it. I am currently just testing the gamertag (username), body text, and timestamp. Here are my classes:
1) NewPost (create a new post that is sent to the table view)
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class NewPost: UIViewController, UITextViewDelegate {
#IBOutlet var enterGamertag: UITextField!
#IBOutlet var enterMessage: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//ADDTOLIST BUTTON
#IBAction func addToList(_ sender: UIButton) {
// guard let userProfile = UserService.currentProfile else {
return }
let postRef =
Database.database().reference().child("posts").childByAutoId()
let postObject = [
// "Gametag": [
//// "uid": userProfile.id,
//// "gamertag": userProfile.gamerTag
// ],
"gamerTag": enterGamertag.text as Any,
"bodytext": enterMessage.text as Any,
"timestamp": [".sv":"timestamp"]
] as [String:Any]
postRef.setValue(postObject, withCompletionBlock: { error, ref in
if error == nil {
self.dismiss(animated: true, completion: nil)
} else {
// Handle the error
}
})
// UserService.sharedInstance.validateUsername("Ninja")
}
//dismiss keyboard
#IBAction func dismissKeyboard(_ sender: UITextField) {
self.resignFirstResponder()
}
#IBAction func micPressed(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
}
#IBAction func logOutPressed(_ sender: UIButton) {
try! Auth.auth().signOut()
// performSegue(withIdentifier: "logOut", sender: self)
}
}
2) feedTable (shows the table view)
import UIKit
import Firebase
class FeedTable: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableFeedView: UITableView!
var posts = [Post]()
//VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
// Hide the navigation bar on the this view controller
tableFeedView.delegate = self
tableFeedView.dataSource = self
tableFeedView.register(UINib(nibName: "PostTableViewCell", bundle: nil), forCellReuseIdentifier: "customTableCell")
// self.tableFeedView?.backgroundColor = UIColor.black
tableFeedView.tableFooterView = UIView()
configureTableView()
}
func observePosts() {
let postRef = Database.database().reference().child("posts")
postRef.observe(.value, with: { snapshot in
var tempPosts = [Post]()
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot,
let dict = childSnapshot.value as? [String:Any],
let gamerTag = dict["gamerTag"] as? String,
let bodytext = dict["bodytext"] as? String,
let timestamp = dict["timestamp"] as? Double {
let post = Post(id: childSnapshot.key, gamerTag: gamerTag, bodyText: bodytext, timestamp: timestamp)
tempPosts.append(post)
}
}
self.posts = tempPosts
self.tableFeedView.reloadData()
})
}
#IBAction func refreshTable(_ sender: UIButton) {
tableFeedView.reloadData()
}
//Cell For Row At
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:PostTableViewCell = tableView.dequeueReusableCell(withIdentifier: "customTableCell", for: indexPath) as! PostTableViewCell
cell .set(post: posts[indexPath.row])
return cell
}
//Number Of Rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
//Automatic Row Height
func configureTableView() {
tableFeedView.rowHeight = UITableViewAutomaticDimension
tableFeedView.estimatedRowHeight = 120.0
}
}
3) PostTableViewCell (the cell that contains the text labels)
import UIKit
class PostTableViewCell: UITableViewCell {
#IBOutlet weak var customMessageBody: UILabel!
#IBOutlet weak var customConsole: UILabel!
#IBOutlet weak var ifMicUsed: UIImageView!
#IBOutlet weak var timeAdded: UILabel!
#IBOutlet weak var gameMode: UILabel!
#IBOutlet weak var customGamerTag: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func set(post:Post){
customGamerTag.text = post.gamerTag
customMessageBody.text = post.bodyText
customMessageBody.text = "\(post.timestamp) minutes ago."
}
}

How to segue back to filled-in version of TableViewController (Swift)?

I am building a room-booking app for iOS in Xcode 9.3.
Here is the basic layout:
First TableViewController(TVC1): starts empty. Pressing '+' pops up the
Second TableViewController(TVC2) with many fields to fill in.
Once the 'Done' button on TVC2 is pressed I get back to TVC1 which now has a cell (Subtitle style) containing the details inserted.
I would now like to tap on said cell and get back to TVC2 to either check or modify the data.
I have created the segue but upon tapping I get the same version of TVC2 that I get when pressing '+', not the filled in one.
What am I doing wrong?
This is the code relative to TVC1 that I need to edit:
import UIKit
class RegistrationTableViewController: UITableViewController {
var registrations: [Registration] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return registrations.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RegistrationCell", for: indexPath)
let registration = registrations[indexPath.row]
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
cell.textLabel?.text = registration.firstName + " " + registration.lastName
cell.detailTextLabel?.text = dateFormatter.string(from: registration.checkInDate) + " - " + registration.roomType.name
return cell
}
#IBAction func unwindFromAddRegistration(unwindSegue: UIStoryboardSegue) {
guard let addRegistrationTableViewController = unwindSegue.source as? AddRegistrationTableViewController,
let registration = addRegistrationTableViewController.registration else { return }
registrations.append(registration)
tableView.reloadData()
}
// MARK: Challenge.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewReservationDetails" {
}
}
And here is the code for (TVC2) so that you have more or less all the app available.
import UIKit
class AddRegistrationTableViewController: UITableViewController, SelectRoomTypeTableViewControllerDelegate {
// MARK: Properties
let checkInDatePickerCellIndexPath = IndexPath(row: 1, section: 1)
let checkOutDatePickerCellIndexPath = IndexPath(row: 3, section: 1)
var isCheckInDatePickerShown: Bool = false {
didSet {
checkInDatePicker.isHidden = !isCheckInDatePickerShown
}
}
var isCheckOutDatePickerShown: Bool = false {
didSet {
checkOutDatePicker.isHidden = !isCheckOutDatePickerShown
}
}
var roomType: RoomType?
var registration: Registration? {
guard let roomType = roomType else { return nil }
let firstName = firstNameTextField.text ?? ""
let lastName = lastNameTextField.text ?? ""
let email = emailTextField.text ?? ""
let checkInDate = checkInDatePicker.date
let checkOutDate = checkOutDatePicker.date
let numberOfAdults = Int(numberOfAdultsStepper.value)
let numberOfChildren = Int(numberOfChildrenStepper.value)
let hasWifi = wifiSwitch.isOn
return Registration(firstName: firstName, lastName: lastName, emailAddress: email, checkInDate: checkInDate, checkOutDate: checkOutDate, numberOfAdults: numberOfAdults, numberOfChildren: numberOfChildren, roomType: roomType, wifi: hasWifi)
}
var selectedItem: Registration?
// MARK: Outlets
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var checkInDateLabel: UILabel!
#IBOutlet weak var checkInDatePicker: UIDatePicker!
#IBOutlet weak var checkOutDateLabel: UILabel!
#IBOutlet weak var checkOutDatePicker: UIDatePicker!
#IBOutlet weak var numberOfAdultsLabel: UILabel!
#IBOutlet weak var numberOfAdultsStepper: UIStepper!
#IBOutlet weak var numberOfChildrenLabel: UILabel!
#IBOutlet weak var numberOfChildrenStepper: UIStepper!
#IBOutlet weak var roomTypeLabel: UILabel!
#IBOutlet weak var wifiSwitch: UISwitch!
// MARK: Actions
#IBAction func datePickerValueChanged(_ sender: UIDatePicker) {
updateDateViews()
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
updateNumberOfGuests()
}
#IBAction func wifiSwitchChanged(_ sender: UISwitch) {
// implemented later
}
#IBAction func cancelButtonTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
// MARK: Methods
func updateDateViews() {
checkOutDatePicker.minimumDate = checkInDatePicker.date.addingTimeInterval(86400)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
checkInDateLabel.text = dateFormatter.string(from: checkInDatePicker.date)
checkOutDateLabel.text = dateFormatter.string(from: checkOutDatePicker.date)
}
func updateNumberOfGuests() {
numberOfAdultsLabel.text = "\(Int(numberOfAdultsStepper.value))"
numberOfChildrenLabel.text = "\(Int(numberOfChildrenStepper.value))"
}
func updateRoomType() {
if let roomType = roomType {
roomTypeLabel.text = roomType.name
} else {
roomTypeLabel.text = "Not Set"
}
}
func didSelect(roomType: RoomType) {
self.roomType = roomType
updateRoomType()
}
override func viewDidLoad() {
super.viewDidLoad()
let midnightToday = Calendar.current.startOfDay(for: Date())
checkInDatePicker.minimumDate = midnightToday
checkInDatePicker.date = midnightToday
updateDateViews()
updateNumberOfGuests()
updateRoomType()
}
// MARK: TableView Data
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch (indexPath.section, indexPath.row) {
case (checkInDatePickerCellIndexPath.section, checkInDatePickerCellIndexPath.row):
if isCheckInDatePickerShown {
return 216.0
} else {
return 0.0
}
case (checkOutDatePickerCellIndexPath.section, checkOutDatePickerCellIndexPath.row):
if isCheckOutDatePickerShown {
return 216.0
} else {
return 0.0
}
default:
return 44.0
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
switch (indexPath.section, indexPath.row) {
case (checkInDatePickerCellIndexPath.section, checkInDatePickerCellIndexPath.row - 1):
if isCheckInDatePickerShown {
isCheckInDatePickerShown = false
} else if isCheckOutDatePickerShown {
isCheckOutDatePickerShown = false
isCheckInDatePickerShown = true
} else {
isCheckInDatePickerShown = true
}
tableView.beginUpdates()
tableView.endUpdates()
case (checkOutDatePickerCellIndexPath.section, checkOutDatePickerCellIndexPath.row - 1):
if isCheckOutDatePickerShown {
isCheckOutDatePickerShown = false
} else if isCheckInDatePickerShown {
isCheckInDatePickerShown = false
isCheckOutDatePickerShown = true
} else {
isCheckOutDatePickerShown = true
}
tableView.beginUpdates()
tableView.endUpdates()
default:
break
}
}
// MARK: Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SelectRoomType" {
let destinationViewController = segue.destination as? SelectRoomTypeTableViewController
destinationViewController?.delegate = self
destinationViewController?.roomType = roomType
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The challenge to this tutorial says to start from here and:
"Update the RegistrationTableViewController(TVC1) with a segue that allows the user to select and view the details of a registration in the AddRegistrationTableViewController(TVC2).
Hope this helps to provide the right solution.
Create a property let say selectedItem in TVC2 screen.
var selectedItem: Registration?
Modify prepare for cell ...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewReservationDetails" {
let sourceCell = sender as! UITableViewCell
if let indexPath = tableView.indexPath(for: sourceCell) {
if let dest = segue.destination as? <#Destination view controller#> {
dest.selectedItem = registrations[indexPath.row]
}
}
}
}
And finally in your destination view controller (having TVC2) you need to check selectedItem and assign value in viewDidLoad: or wherever.
override func viewDidLoad() {
super.viewDidLoad()
if let item = selectedItem {
//here set value as you want to views
}
}
Use this code in tableView(_:didSelectRowAt:):
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "ViewReservationDetails", sender: indexPath)
}
Then, in TVC1’s prepare(for:sender:) you pass the selected registrations element plus a completion handler to TVC2, like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewReservationDetails" {
if let destination = segue.destination as? AddRegistrationTableViewController, let indexPath = sender as? IndexPath {
destination.registration = registrations[indexPath.row]
destination.completionHandler = { (registration) in
self.registrations.append(registration)
self.tableView.reloadData()
}
}
}
}
In TVC2, you declare a property completionHandler like this:
var completionHandler: ((Registration) -> Void)?
and you call this handler in viewWillDisappear:
completionHandler?(newRegistration)
with newRegistration being the newly created element to be added to the Registration array. This way, you won’t need an unwind segue.

Can't save data to MVC (Swift)

I can't save my data from textfield to MVC.
When I enter text and click on the button, the data is not added.
how can i fix it?
I delete viewDidLoad() with table.delegate = self, table.dataSource = self from this Question.
This is the ViewController:
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var table: UITableView!
#IBOutlet weak var Text: UITextField!
var model = ViewModel()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.persons?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
let person = model.persons?[indexPath.row]
cell.Name?.text = person?.name
return cell
}
func textFieldEditing(_ sender: UITextField) {
model.input = sender.text
}
#IBAction func Add(_ sender: UIButton) {
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}
}
Model
class ViewModel {
var persons: [Person]?
var input: String?
func addPerson(_ person: Person) {
if persons == nil { persons = [] }
persons?.append(person)
}
}
class Person {
var name: String?
}
try this
// func textFieldEditing(_ sender: UITextField) {
// model.input = sender.text
// }
func textFieldDidEndEditing(_ textField: UITextField) {
model.input = textField.text
}
#IBAction func Add(_ sender: UIButton) {
self.view.endEditing(true)
if let name = model.input, name.count > 0 {
let person = Person()
person.name = name
model.addPerson(person)
model.input = nil
}
table.reloadData()
}

Resources