Change values in dictionary based on which cell selected in TableView - arrays

When clicking a cell from the tableView, the cells data (fetched from an array) gets passed on to the 'Detail View Controller' to be displayed in labels. When pressing edit on the Detailview, data gets passed to the 'Edit View Controller' textfields.
When editing those textfields and pressing "Save" I want this data to overwrite the current data in the arrays dictionary based on which cell that was pressed in the tableView.
What would be the best approach to this? Right now data gets passed all the way to the 'EditViewController', but not back to the corresponding dictionary in array when saved.
Main ViewController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // Set up Delegate and Data Source for Table View
#IBOutlet weak var tradeTableView: UITableView!
var tradesList = TradeList()
// Go to detail view of trade when pressing its tableview cell
#IBSegueAction func showDetailView(_ coder: NSCoder) -> DetailViewController? {
guard let indexPath = tradeTableView.indexPathForSelectedRow
else { fatalError("Nothing selected!")}
let trade = tradesList.trades[indexPath.row]
return DetailViewController(coder: coder, trade: trade)
}
override func viewDidLoad() {
super.viewDidLoad()
// Set the table view as the delegate and data source
tradeTableView.dataSource = self
tradeTableView.delegate = self
}
// Delegating functions for Table View
func numberOfSections(in tableView: UITableView) -> Int {
1
}
// Delegating functions for Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tradesList.trades.count
}
// Delegating functions for Table View
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(TradeCell.self)", for: indexPath) as? TradeCell
else { fatalError("Could not create TradeCell")}
let trade = tradesList.trades[indexPath.row]
// Text to display in cells 'ticker' and 'name' label
cell.tickerLabel?.text = trade.ticker
cell.nameLabel?.text = trade.name
return cell
}
}
DetailViewController:
class DetailViewController: UIViewController {
let trade: Trade
#IBOutlet var tickerLabel: UILabel!
#IBOutlet var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Changes labels according to which cell was pressed in ViewController
tickerLabel.text = trade.ticker
nameLabel.text = trade.name
}
// Initializers
required init?(coder: NSCoder) { fatalError("This should never be called!")}
required init?(coder: NSCoder, trade: Trade) {
self.trade = trade
super.init(coder: coder)
}
// Edit button tapped
#IBAction func editTapped(_ sender: Any) {
self.performSegue(withIdentifier: "DetailVCToEditVC", sender: self)
}
// Prepare data to pass to 'EditViewController'
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "DetailVCToEditVC"){
let displayVC = segue.destination as! EditViewController
displayVC.editTitle = tickerLabel.text
displayVC.editPrice = nameLabel.text
}
}
}
EditViewController:
class EditViewController: UIViewController {
// Variables recieving passed data from 'DetailViewController'
var editTitle: String?
var editPrice: String?
#IBOutlet weak var editTitleField: UITextField!
#IBOutlet weak var editPriceField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Setting the textfields text according to the passed data from 'DetailViewController'.
editTitleField.text = editTitle
editPriceField.text = editPrice
}
#IBAction func editSaveButton(_ sender: UIButton) {
// Dismisses edit screen overlay
self.dismiss(animated: true, completion: nil);
}
}
My array is as follows in another swift file:
struct TradeList {
let trades: [Trade] = [
Trade(ticker: "AAPL", name: "Apple"),
Trade(ticker: "AMD", name: "Advanced Micro Devices")
]
}

Use singleton pattern to update data. You don't need to pass data to view controllers. It will update automatically. Here is how your trade list struct should be
struct TradeList {
static let shared = TradeList(trades: [
Trade(ticker: "AAPL", name: "Apple"),
Trade(ticker: "AMD", name: "Advanced Micro Devices")
])
var trades: [Trade] = []
}
U can use it as following anywhere
for getting values
print(TradeList.shared.trades)
for updating values
TradeList.shared.trades = [...]//Any value here

Related

How do I get the index of an array to properly save a unique value to Firebase?

In each row of a tableview there is a label and button. The label displays a quotes from an array. Users can tap the button to save the quote. Right now it works fine with UserDefaults, but I want to also save the information to Firebase. I can't seem to figure out how to get the quote to save to Firebase based on the heart/row that was tapped. I thought I could use IdexPath, but I can't seem to get it right. I'd like to save each quote as a unique value in Firebase and be able to delete it when the button is tapped again. However, I'm not so familiar with firebase.
I thought I could use IdexPath to determine which row was selected and then grab the label in that row to send to Firebase, but I can't seem to get it right. Instead I got an error preventing the code from running "Instance member 'row' cannot be used on type 'IndexPath'; did you mean to use a value of this type instead?"
import UIKit
import FirebaseDatabase
import FirebaseAuth
class QuotesMainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var quotesTableView: UITableView!
struct Quote: Codable {
var label: String
var like: Bool = false // a priori, false
}
var quoteList: [Quote] = []
override func viewDidLoad() {
super.viewDidLoad()
quotesTableView.delegate = self
quotesTableView.dataSource = self
let defaults = UserDefaults.standard
if let data = defaults.data(forKey: "QuoteListKey") {
if let array = try? PropertyListDecoder().decode([Quote].self, from: data) {
quoteList = array
}
} else {
quoteList = [Quote(label: "Quote1"), Quote(label: "Quote2"), Quote(label: "Quote3")]
}
}
#IBAction func likeTapped(_ sender: UIButton) {
var ref: DatabaseReference?
ref = Database.database().reference()
quoteList[sender.tag].like.toggle() // update the dataSource ; sender.tag gives the row in the array
if quoteList[sender.tag].like {
sender.setImage(UIImage(named: "GreenHeart"), for: .normal) // You can change here or ask for a reloadData()
guard let user = Auth.auth().currentUser?.uid else { return }
ref!.child("users").child(Auth.auth().currentUser!.uid).child("Quotes").setValue(quoteList[IndexPath.row].label)
if let data = try? PropertyListEncoder().encode(quoteList) {
UserDefaults.standard.set(data, forKey: "QuoteListKey")
}
} else {
sender.setImage(UIImage(named: "blankHeart"), for: .normal)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return quoteList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = quotesTableView.dequeueReusableCell(withIdentifier: "cell") as! QuotesTableViewCell
cell.quoteLabel.text = quoteList[indexPath.row].label
cell.likeButton.tag = indexPath.row // Use tag to reference the cell, not to set true / false
cell.likeButton.setImage(UIImage(named: quoteList[indexPath.row].like ? "GreenHeart" : "blankHeart"), for: .normal)
return cell
}
}

How to add values to array dynamically if you move from one view controller to another viewcontroller in swift?

In Add_EditAddressViewController i need to show all added address in tableview, for that i have created one ARRAY and appending values to array in NewZoomAddressViewController to show in tableview but all the time i am getting single row in table view.. so here how to add value to array dynamically without replacing into oldvalue in Add_EditAddressViewController
and navigation is:
Add_EditAddressViewController: butnTap -> ProfileVC: btnTap -> NewZoomAddressViewController: btnTap -> Add_EditAddressViewController
here each time when i come to NewZoomAddressViewController need to append \(self.sublocalityName!) \(localityName!) \(self.zipName!) to addressArray to show in tableview of Add_EditAddressViewController
Note: here i have added this question related code in github: https://github.com/SwiftSamples/AddressBug here in profileVC you need to tap on map or continue Button then it navigates to NewZoomAddressViewController
class Add_EditAddressViewController: UIViewController,DataEnteredDelegate {
#IBOutlet weak var addeditTableview: UITableView!
var addressArray = [String]()
var city: String?
var pincode: String?
var locality: String?
override func viewDidLoad() {
super.viewDidLoad()
addeditTableview.register(UINib(nibName: "EditAddressTableViewCell", bundle: nil), forCellReuseIdentifier: "EditAddressTableViewCell")
print("zoooom valuew \(pincode)")
addeditTableview.reloadData()
}
}
extension Add_EditAddressViewController : UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return addressArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: EditAddressTableViewCell = tableView.dequeueReusableCell(withIdentifier: "EditAddressTableViewCell") as! EditAddressTableViewCell
cell.editButton.addTarget(self, action: #selector(editbuttonClicked(sender:)), for: .touchUpInside)
cell.nameHeader.text = "header"
cell.addressLabel.text = addressArray[indexPath.row]
return cell
}
}
NewZoomAddressViewController code:
class NewZoomAddressViewController: UIViewController {
weak var delegate: DataEnteredDelegate? = nil
var addressModel: ProfileModelUserAddress?
var addressArray = [String]()
var zipName: String?
var localityName: String?
var sublocalityName: String?
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var addressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("in Zoom map VC")
mapView.delegate = self
addressLabel.text = "\(self.sublocalityName!) \(localityName!) \(self.zipName!)"
}
#IBAction func confirmBtn(_ sender: Any) {
let viewController = storyboard?.instantiateViewController(withIdentifier: "Add_EditAddressViewController") as! Add_EditAddressViewController
addressArray.append("\(sublocalityName ?? "") \(zipName ?? "") \(localityName ?? "")")
viewController.addressArray = addressArray
print("total address array all rows \(viewController.addressArray)")
navigationController?.pushViewController(viewController, animated: true)
}
}
please try to help to display all added address in tableview. i got stuck here from long time.
In your NewZoomAddressViewController replace confirm button action with
#IBAction func confirmBtn(_ sender: Any) {
for controller in navigationController?.viewControllers ?? [] {
if let listController = controller as? Add_EditAddressViewController {
let string = "\(sublocalityName ?? "") \(zipName ?? "") \(localityName ?? "")"
listController.addressArray.append(string)
navigationController?.popToViewController(controller, animated: true)
return
}
}
}
In Add_EditAddressViewController reload TableView on viewWillAppear
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden=true
addeditTableview.reloadData()
}
Well what you need to do is to have address array in your profile view as well to pass it to other controller.. so your code becomes
First you will have array in profile like this
class ProfileAddressViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, DataEnteredDelegate {
var addressArray = [String]()
}
Then when you call NewZoomAddressViewController you pass that array to them like this
#objc func triggerTouchAction(_ sender: UITapGestureRecognizer) {
print("Please Help!")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NewZoomAddressViewController") as! NewZoomAddressViewController
viewController.delegate = self
viewController.zipName = self.pincodeField.text
viewController.sublocalityName = self.colonyField.text
viewController.localityName = self.cityField.text
viewController.addressArray = addressArray
self.navigationController?.pushViewController(viewController, animated: true);
}
And in your Add_EditAddressViewController where you call profile.. assign array to profile
#objc func editbuttonClicked(sender: UIButton) {
print("in button")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "ProfileAddressViewController") as! ProfileAddressViewController
viewController.addressArray = addressArray
self.navigationController?.pushViewController(viewController, animated: true)
}

Item appends to array in one view controller but not in another

I have a TableView that displays a list of entries in the cells. The entries are stored in an array. The user enters in text for the entries in another view controller (modal), hits "Save", and after .reloadData(), the new entry should append to the array and display in the TableView. The array lives in the HomeController.
To pinpoint where the problem might be, I've tried appending some text in the HomeController, then appending to the same array from the NotesController. In the latter, when I print homeController.entryInput, I'm expecting ["hello", "goodbye"]. Instead, I only get ["goodbye"].
Also, when I click on "Save" in NotesController a second time, I get ["goodbye"] again instead of ["goodbye", "goodbye"]. So it looks like the array gets overrided.
I've also tried hard coding items into the array, and they show up fine in the TableView. When I append an item from NotesController, it appends to the array but doesn't show on the TableView, and when I add another item from the same ViewController, it overwrites the first item I appended rather than adding it as a new item.
HomeController:
class HomeController: UIViewController {
let tableView = UITableView()
var entryInput: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
self.entryInput.append("hello")
print(entryInput)
tableView.register(HomeCell.self, forCellReuseIdentifier: reuseIdentifier)
let height = view.frame.height
tableView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
tableView.backgroundColor = .white
view.addSubview(tableView)
}
extension HomeController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return entryInput.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! HomeCell
cell.entryTextLabel.text = entryInput[indexPath.row]
return cell
}
NotesController:
class NotesController: UIViewController {
let homeController = HomeController()
...
let saveBtn = UIView().navigationBtn(text: "Save")
let homeController = HomeController()override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
saveBtn.addTarget(self, action: #selector(save(sender:)), for: .touchUpInside)
}
#objc func save(sender: UIButton) {
homeController.entryInput.append("goodbye")
homeController.tableView.reloadData()
print(homeController.entryInput)
dismiss(animated: true, completion: nil)
}
}
I've looked all around Stack Overflow and other websites about appending to arrays but for some reason can't seem to figure out why I can't append to an array in another class.
Because you have create new HomeController in NotesController that doesn't reference to your current HomeController.
class NotesController: UIViewController {
let homeController = HomeController()
}
When HomeController want to open NotesController.
And NotesController want to communicate back to HomeController.
You should pass HomeController reference to NotesController.
func routeToNotesController() {
let vc = NotesViewController()
vc.homeController = self
let nc = UINavigationController(rootViewController: vc)
present(nc, animated: true)
}
Answer your questions.
From the NotesController after save called. You're expecting
["hello","goodbye"] but you get ["goodbye"] instead.
Because you are instantiate HomeController in
NotesViewController The homeController that you create is not
the same HomeController appear on the screen, It's new so
homeController and entryInput still empty because viewDidLoad isn't
call.
When you add another item from NotesViewController, You're
expecting ["goodbye", "goodbye"] but you get ["goodbye"] instead.
According the first answer. You create new homeController(that not
relate to existing one on the screen) inside NotesController
every time it appear on the screen.
Here is the result.
Left image show HomeController.
Middle image show HomeController that open InputsController.
Right image show HomeController that updated by InputsController.
My implementation.
class ListViewController: UITableViewController {
var entryInput: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "ListViewController"
tableView.backgroundColor = .white
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add))
entryInput.append("hello")
tableView.reloadData()
}
#objc func add() {
let vc = InputViewController()
vc.listViewController = self
let nc = UINavigationController(rootViewController: vc)
present(nc, animated: true)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return entryInput.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = entryInput[indexPath.row]
return cell
}
}
class InputViewController: UIViewController {
weak var listViewController: ListViewController?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "InputViewController"
view.backgroundColor = .white
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save))
}
#objc func save() {
listViewController?.entryInput.append("goodbye")
listViewController?.tableView.reloadData()
dismiss(animated: true)
}
}
It looks like the line:
let homeController = HomeController()
creates a new instance of the class HomeController and does not refer to the already existing ViewController. Which means: modifying the array in the new instance of HomeController won't affect your main ViewController.
This also leads to the answer of your question about the missing "hello"-element in your array. When you create the object homeController it's array does not have any elements.
When #objc func save(sender: UIButton) get's called, you append "goodbye" to the empty array.
By calling dismiss(...) you are returning to the original version of your HomeController() which does not know anything about the other instance you created in NotesController().
To achieve what you are trying to do, I recommend reading more about how to pass data between ViewControllers:
How do you share data between view controllers and other objects in Swift?
https://www.hackingwithswift.com/example-code/system/how-to-pass-data-between-two-view-controllers

Save text in array and display in a table view

I am trying to create an app that displays a table view based on an array of strings.
I have one view controller and a smaller content view within it. In the view controller there is a text field and a button that should save the written text in an array and display it in the table view controller. As well as embedding it in the content view.
I don't know how to save the written text and to add it in the array, perhaps using append.
How can I display the array in the table view and to save the array in NSUserDefaults?
EDIT:
Here's an image of the view controller and the content view. I want to insert one string in the text field (the one over the green button Save), then I tap the green button and the string I wrote is added in the array and displayed in a table view cell of the table view controller embed in the content view. At the same time, the text field return empty, but I already know how to clear it. Then, I can re-write texts in the text field and it should repeats the actions I just described.
A the moment isn't so important to save in NSUserDefaults.
Thanks for the help. :)
http://i.stack.imgur.com/z5uTc.png
EDIT 2:
MainVC
import UIKit
class mainVC: UIViewController {
#IBOutlet var txtField: UITextField!
var embTableVC: tableVC!
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.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "embededTableVC" {
embTableVC = segue.destinationViewController as! tableVC
}
}
#IBAction func Save() {
if let Text = txtField.text {
if txtField.text == "" {
myArray.append(Text)
let row = myArray.count-1
let indexPath = NSIndexPath(forRow: row, inSection: 0)
embTableVC.myTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
txtField.text = ""
txtField.resignFirstResponder()
}
}
TableVC
import UIKit
var myArray = [String]()
class tableVC: UITableViewController {
#IBOutlet var myTableView: UITableView! {
didSet {
myTableView.dataSource = self
myTableView.delegate = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
myTableView.dataSource = self
myTableView.delegate = self
myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "customcell")
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier("customcell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = myArray[indexPath.item]
return cell
}
Thanks a lot :)
To store the information:
// Get the standardUserDefaults object, store your UITableView data array against a key, synchronize the defaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:arrayOfImage forKey:#"tableViewDataImage"];
[userDefaults setObject:arrayOfText forKey:#"tableViewDataText"];
[userDefaults synchronize];
To retrieve the information:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSArray *arrayOfImages = [userDefaults objectForKey:#"tableViewDataImage"];
NSArray *arrayOfText = [userDefaults objectForKey:#"tableViewDataText"];
// Use 'yourArray' to repopulate your UITableView
On first load, check whether the result that comes back from NSUserDefaults is nil, if it is, you need to create your data, otherwise load the data from NSUserDefaults and your UITableView will maintain state.
In Swift, the following approach can be used:
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(arrayOfImage, forKey:"tableViewDataImage")
userDefaults.setObject(setObject:arrayOfText, forKey:"tableViewDataText")
userDefaults.synchronize()
var arrayOfImages = userDefaults.objectForKey("tableViewDataImage")
var arrayOfText = userDefaults.objectForKey("tableViewDataText")
Hope this helps. You can also use a xcdatamodeld to save and retrieve data.
Here's a simple solution for storing the text in an array, a better option than NSUserDefaults if you might have a large number of Strings.
First, you will need to have an Array of Strings in the View Controller managing the Table View. Then, you will need a way to access that Array and edit it.
I would store a reference to the table view controller within the first view controller (with the container). To first set this reference, use the embed segue.
In your storyboard, the arrow connecting the first VC to the table VC is actually an embed segue that fires upon load of the container view. Click the segue, and in the attributes inspector in Xcode change the identifier to some String such as "embedTableVC".
Then we can set the reference in the first view controller. Here's some relevant code, assuming the view controller with the container has a class of MainViewController and the table view controller within the container has a class of TableViewController:
class MainViewController: UIViewController {
var embededTableVC: TableViewController!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "embedTableVC" {
embededTableVC = segue.destinationViewController as! TableViewController
}
}
}
Once the main view controller loads, the container will be loaded, which will then cause the segue to run, which will cause prepareForSegue to be called. In this implementation, we are storing the table view controller in a property on our main view controller so we can access it. Since classes are reference types, this property will refer to the same object, not a copy.
Then, you get the text from the text field once the save button is pressed, and set it to the array in the table VC, like this:
#IBAction func save() {
if let text = textField.text {
if text != "" {
embededTableVC.valueArray.append(text)
// and if you want to go ahead and add it to the array from here instead of using delegation or notification observance
let row = embededTableVC.valueArray.count - 1
let indexPath = NSIndexPath(forRow: row, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
}
The if-let syntax ensures the text property of the text field is not nil.
I believe this is what you were looking for, but keep in mind that everything will be gone upon app relaunch, since we're not using NSUserDefaults or Core Data to store anything to the device's drive. You'll want a combination of the two approaches if you need persistence.
Edit:
As far as populating the table view with content from the array, you should consult Apple's Docs, as explaining it here would get pretty lengthy and the information may be found easily. Alternatively, you can check out this article to get an understanding for populating the table view, or see this question. You will need to implement numberOfSectionsInTableView, numberOfRowsInSection, and cellForRowAtIndexPath at a minimum for your table view.
Edit 2:
After reading your code, I'd say there are many things that probably ought to be changed eventually, but just to get it to work for now, the following changes need made for now:
Change this (from Save())…
if let Text = txtField.text {
if txtField.text == "" {
myArray.append(Text)
let row = myArray.count-1
let indexPath = NSIndexPath(forRow: row, inSection: 0)
embTableVC.myTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
…to this
if let text = txtField?.text {
if text != "" { // Notice the two changes on this line
myArray.append(text)
let row = myArray.count - 1
let indexPath = NSIndexPath(forRow: row, inSection: 0)
embTableVC.myTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
Change this (in cellForRowAtIndexPath)…
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier("customcell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = myArray[indexPath.item]
return cell
}
…to this
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier("customcell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = myArray[indexPath.row] // Notice `row`, not item`
return cell
}
If you're still getting a crash because a nil optional was unwrapped, which is highly possible, I need to know what line Xcode crashes on and I need to know what the error says.
import UIKit
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
**var message = ["a","b"]
var toPass: String!**
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
**message.append(toPass)**
}
#IBAction func SendButon(_ sender: UIButton) {
performSegue(withIdentifier: "segue2", sender: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// tableView.reloadData()
return message.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(UINib(nibName: "TableViewCell", bundle: nil),
forCellReuseIdentifier: "CellFor")
let cell = tableView.dequeueReusableCell(withIdentifier: "CellFor", for: indexPath) as! TableViewCell
cell.labelView.text = message[indexPath.row]
return cell
}
}

SWIFT - UIimagepicker to assign image into an array

Basically I would like my Imagepicker to be able to assign the captured a image to a new row in tableview each time the user input a name and select a image for this name. I encounter at least 2 types of errors for below codes:
1) 'UIImageView' is not a subtype of 'NSString' error being displayed besides "cell.itemImage.image = UIImage(named: selectedImageArray[indexPath.row])"
2) a problem of how to access for example '.contentMode' and '.clipsToBounds' properties of the assigned image (being each in the array to be assigned to the tableview)
Appreciate anyone's help on these~~
Tableview Controller:
import UIKit
class AddPostItemTableViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITableViewDelegate {
#IBOutlet var titleName: UILabel!
#IBOutlet var tapCamera: UIImageView!
#IBOutlet var tapLibrary: UIImageView!
#IBOutlet weak var itemNameField:UITextField!
#IBOutlet weak var AddPostTableView:UITableView!
var selectedImageArray:[UIImageView!] = []
var selectedItemNameArray:[String!] = []
let tapCameraRec = UITapGestureRecognizer()
let tapLibraryRec = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
tapCameraRec.addTarget(self, action: "tappedCamera")
tapLibraryRec.addTarget(self, action: "tappedLibrary")
tapCamera.addGestureRecognizer(tapCameraRec)
tapLibrary.addGestureRecognizer(tapLibraryRec)
tapLibrary.userInteractionEnabled = true
tapCamera.userInteractionEnabled = true
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
// Dismiss keyboard on touch
}
func tappedLibrary(){
if itemNameField.text == "" {
let alertController = UIAlertController(title: "Oops", message: "Please key in the name of item first", preferredStyle: .Alert)
let doneAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(doneAction)
self.presentViewController(alertController, animated: true, completion: nil)
return
}
else if UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
imagePicker.sourceType = .PhotoLibrary
self.presentViewController(imagePicker, animated: true, completion: nil)
}
}
func tappedCamera(){
if itemNameField.text == "" {
let alertController = UIAlertController(title: "Oops", message: "Please key in the name of item first", preferredStyle: .Alert)
let doneAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(doneAction)
self.presentViewController(alertController, animated: true, completion: nil)
return
}
else if UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
imagePicker.sourceType = .Camera
self.presentViewController(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image:UIImageView!, editingInfo: [NSObject : AnyObject]!) {
selectedImageArray.append(image)
selectedImageArray.contentMode = UIViewContentMode.ScaleAspectFill
selectedImageArray.clipsToBounds = true
selectedItemNameArray.append(itemNameField!.text)
dismissViewControllerAnimated(true, completion: nil)
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// Return the number of rows in the section.
return self.selectedItemNameArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:
NSIndexPath) -> UITableViewCell {
let cellIdentifier = "ItemCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath:
indexPath) as AddPostTableViewCell
// Configure the cell...
cell.itemName.text = selectedItemNameArray[indexPath.row]
cell.itemImage.image = UIImage(named: selectedImageArray[indexPath.row])
return cell
}
Tableview Cell:
import UIKit
class AddPostTableViewCell: UITableViewCell {
#IBOutlet weak var itemName:UILabel!
#IBOutlet weak var itemImage:UIImageView!
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
}
}
The first error suggests that your array contains UIImageViews, not names of images. The UIImage(imageNamed: ) accepts name of image as a string. You probably need something like
cell.itemImage.image = selectedImageArray[indexPath.row].image
or if you want to use the UIImage(imageNamed:), use your name array instead.
With regards to the second issue, you can put a dot after the [indexPath.row] to access properties of the stored object at the given index like I did above. Or you can do it in a more readable way:
var myImage = selectedImageArray[indexPath.row]
myImage.someProperty

Resources