SWIFT - UIimagepicker to assign image into an array - arrays

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

Related

Change values in dictionary based on which cell selected in TableView

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

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)
}

How to pull out Data Array from CoreData TableView array, put it into a new Array and export it as csv?

I have an app that takes user inputs in an alertController, and then this values will be stored in CoreData which is then displayed in a tableview. I concatenated all the strings to gather with a comma as separator to make it easier for me to export a csv. However, when I print out the CoreData entity, I get an array that is quite complicated. The array looks like this:
[ (entity: AlarmItems; id: 0xc2bccd37cb753acb ; data: {
alarmAttributes = "Example Name, 24/11/2019, 1500, True, NIL";
}), (entity: AlarmItems; id: 0xc2bccd37cb653acb ; data: {
alarmAttributes = "Example , 12/12/2019, 24/11/2019, True, NIL";
})]
I would like to pull out just that parts after alarmAttributes to be exported into a CSV for further use.
I looked at NSEntityMapping but that did not help me. I'm quite stuck right now. I do not know how to approach the problem. Is my approach even correct in the first place? Is it even possible to export a csv using a an array that I create? The idea is to have the csv be stored in the iOS Device which can then be emailed elsewhere.
My ViewController:
class ViewController: UITableViewController {
var alarmItems: [NSManagedObject] = []
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "AlarmItems")
do {
alarmItems = try managedContext.fetch(fetchRequest)
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
#objc func addAlarmItem(_ sender: AnyObject) {
print("this works")
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItems", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
alarmItems.append(item)
} catch let err as NSError {
print("Failed to save an item", err)
}
}
#objc func exportCSV(_ sender: AnyObject) {
//will work on exporting csv in the future
return
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return alarmItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = alarmItems[indexPath.row]
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
}

tableview duplicating data on back button

I am creating individual members in my app, and everything works well. I validate user input, store it in a local array, and then pass the data on to Firebase when I tap 'next'. The app takes me from Step 2 to Step 3. So far so good.
This is what Step 2 looks like if I load the app from scratch: Step 2 Existing Users. The users are pulled from Firebase, and everything is correct.
If I add a new user, everything updates fine. I tap the 'next' button, and Firebase updates with 3 users, all with correct data, and I'm taken to Step 3. All is good still.
But if I tap the 'back' button in the upper left corner of Step 3 (provided by my navigation controller), the app takes me back to Step 2 and now a duplicate entry of the new user shows up in my tableview. Somehow a duplicate user is appended to my array. This only shows up AFTER I click the 'back' button. It doesn't show up in Firebase, but I don't know where it's coming from. I've spent all day trying to figure this out. I'm sure it's a simple problem that I'm overlooking, but I could really use the community's help on this one.
I've looked at this but it didn't help me. I did some tests where I printed out users.count and found that on ViewDidLoad, the count was 0, but on the unwind segue, the count updated to 3 when I added a new user (this was correct, because there were 3 users). But then when I went to Step 3 and tapped the 'back' button to get back to Step 2, Step 2's ViewDidLoad showed a users.count of 4. I have no idea why. I'm completely stumped.
Here is my Step 2 View Controller code in its entirety:
class Step2VC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var usersTableView: UITableView!
var users = [User]() // create variable called 'users' which is an array of type User (which is a class we created)
var firebaseUser: FIRUser!
var firebaseStorage: FIRStorage!
var ref: FIRDatabaseReference!
var cellStyleForEditing: UITableViewCellEditingStyle = .none
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "users"
usersTableView.dataSource = self
usersTableView.delegate = self
usersTableView.tableFooterView = UIView()
// --------
// Firebase
// --------
firebaseUser = FIRAuth.auth()?.currentUser
firebaseStorage = FIRStorage.storage()
ref = FIRDatabase.database().reference().child("users").child(firebaseUser.uid)
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "edit", style: .plain, target: self, action: #selector(editButtonTapped))
loadExistingUsers() // check to see if there are existing users, and if so, load them into tableview
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
usersTableView.reloadData()
}
// ----------
// Table View
// ----------
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! Step2Cell
cell.myLabel.text = users[indexPath.row].firstName
cell.userImage.image = users[indexPath.row].photo
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
deleteUserConfirmationAlert(tableViewIndexPath: indexPath)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "EditUser", sender: users[indexPath.row])
}
// ----------
// Navigation
// ----------
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditUser" {
let nextContoller = segue.destination as! Step2UsersVC
// 'sender' is retrieved from 'didSelectRow' function above
nextContoller.user = sender as? User
nextContoller.navBarTitle = "edit user"
} else if segue.identifier == "AddUser" {
let nextController = segue.destination as! Step2UsersVC
nextController.navBarTitle = "add user"
} else {
print("Segue Initiated:",segue.identifier!)
}
}
#IBAction func unwindToStep2VC(sender: UIStoryboardSegue) {
let sourceVC = sender.source as! Step2UsersVC
let updatedUser = sourceVC.user
if let selectedIndexPath = usersTableView.indexPathForSelectedRow {
// Update an existing user
users[selectedIndexPath.row] = updatedUser!
usersTableView.reloadData()
} else {
// Add a new user
let newIndexPath = IndexPath(row: users.count, section: 0)
users.append(updatedUser!)
usersTableView.insertRows(at: [newIndexPath], with: .automatic)
users.sort(by: {$0.birthday < $1.birthday})
usersTableView.reloadData()
}
}
#IBAction func nextButtonTapped(_ sender: UIButton) {
// check for at least two users
if users.count < 2 {
createAlert(alertTitle: "Users", alertMessage: "You have not created enough users. Please enter in at least two users.")
} else {
// check for at least one parent
if numberOfParents() < 1 {
createAlert(alertTitle: "Users", alertMessage: "You must have at least one parent. Please enter in a parent.")
} else {
confirmationAlert()
}
}
}
// ---------
// Functions
// ---------
// if users exist on Firebase, load them
func loadExistingUsers() {
ref.child("members").observe(.childAdded) { (snapshot: FIRDataSnapshot) in
if let dict = snapshot.value as? [String : Any] {
let userPhotoUrl = dict["profileImageUrl"] as! String
let userFirstName = dict["firstName"] as! String
let userBirthday = dict["birthday"] as! Int
let userPasscode = dict["passcode"] as! Int
let userGender = dict["gender"] as! String
let isUserChildOrParent = dict["childParent"] as! String
let storageRef = FIRStorage.storage().reference(forURL: userPhotoUrl)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
let pic = UIImage(data: data!)
let user = User(profilePhoto: pic!,
userFirstName: userFirstName,
userBirthday: userBirthday,
userPasscode: userPasscode,
userGender: userGender,
isUserChildOrParent: isUserChildOrParent)
self.users.append(user)
self.users.sort(by: {$0.birthday < $1.birthday})
self.usersTableView.reloadData()
})
}
}
}
func saveUsersToFirebase() {
for user in users {
let storageRef = FIRStorage.storage().reference().child("users").child(firebaseUser.uid).child("members").child(user.firstName)
let profileImg = user.photo
let imageData = UIImageJPEGRepresentation(profileImg, 0.1) // compress photos
storageRef.put(imageData!, metadata: nil, completion: { (metadata, error) in
if error != nil {
return
}
// get Firebase image location and return the URL as a string
let profileImageUrl = (metadata?.downloadURL()?.absoluteString)!
// save user data to Firebase
self.ref?.child("members").child(user.firstName).setValue(["profileImageUrl" : profileImageUrl,
"firstName" : user.firstName,
"birthday" : user.birthday,
"passcode" : user.passcode,
"gender" : user.gender,
"childParent" : user.childParent])
})
}
}
func editButtonTapped() {
if cellStyleForEditing == .none {
cellStyleForEditing = .delete
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "done", style: .done, target: self, action: #selector(editButtonTapped))
} else {
cellStyleForEditing = .none
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "edit", style: .plain, target: self, action: #selector(editButtonTapped))
}
usersTableView.setEditing(cellStyleForEditing != .none, animated: true)
}
func deleteUserConfirmationAlert(tableViewIndexPath: IndexPath) {
// create alert for user to confirm user deletion
let alert = UIAlertController(title: "Delete User", message: "Are you sure you want to delete \(users[tableViewIndexPath.row].firstName)? This cannot be undone.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "okay", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
// remove user from Firebase
self.ref.child("members").child(self.users[tableViewIndexPath.row].firstName).removeValue()
self.users.remove(at: tableViewIndexPath.row)
self.usersTableView.deleteRows(at: [tableViewIndexPath], with: .fade)
}))
alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
self.usersTableView.reloadData()
}))
present(alert, animated: true, completion: nil)
}
func numberOfParents() -> Int {
var parentCount = 0
for user in users {
if user.childParent == "parent" {
parentCount += 1
}
}
return parentCount
}
func createAlert(alertTitle: String, alertMessage: String) {
let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "okay", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
func confirmationAlert() {
let alert = UIAlertController(title: "Users", message: "You have entered in \(users.count) users. Are you finished adding family members?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "continue", style: .default, handler: { (action) in
self.saveUsersToFirebase()
self.performSegue(withIdentifier: "GoToStep3", sender: self)
alert.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
}

Append Array with UserDefaults input text

I have an Array of strings which is populating a collection view and that works well. The issue is I want to append that array with Strings that is saved in user defaults from user input textfield. I am getting the UserDefault data, The issue is it is not showing up in seperate collection view cell. it is getting attached at the end of each string in the current cells. Thanks in advance, any help would be appreciated.
This is what I tried so far:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
let defaults = UserDefaults.standard
let value = defaults.string(forKey: "Gratitude")
print(value!)
//Array that I am trying to append with userdefault data
gratitudeArray.append(value!)
// Configure the cell
cell.cellLabel.text = gratitudeArray[indexPath.row]
return cell
}
// I am geeting user input from alert and saving in userdefaults like this:
func presentAlert() {
let alertController = UIAlertController(title: "", message: "Create your own Gratitude:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Save", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store data
UserDefaults.standard.set(field.text, forKey: "Gratitude")
UserDefaults.standard.synchronize()
} else {
print()
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
//print(textField.text!)
//textField.placeholder = ""
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
Store String in ViewDidLoad() like below:
var strValue: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
strValue= defaults.string(forKey: "Gratitude")
}
and display in cellForItemAt like this:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.cellLabel.text = gratitudeArray[indexPath.row] + " " + strValue
return cell
}
If I understand your problem correctly, you want number of cells to increase by one (the value you extract out of UserDefaults). For that, you should append it some place which is outside collection view's data source methods (like viewDidLoad()) and then reload your collection view.
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
strValue = defaults.string(forKey: "Gratitude")
gratitudeArray.append(strValue)
self.collectionView.reloadData()
}
I solved the issue by creating an array in my alert controller to hold user input and then save that array to user defaults.
func presentAlert() {
let alertController = UIAlertController(title: "", message: "Create your own Gratitude:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Save", style: .default) { (_) in
if let field = alertController.textFields {
let textFieldArray = field as [UITextField]
let text = textFieldArray[0].text
let key = "Gratitude"
var myArray = UserDefaults.standard.array(forKey: key) ?? []
myArray.append(String(describing: text!))
UserDefaults.standard.set(myArray, forKey: key)
print(myArray)
} else {
print()
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}

Resources