How can I upload images from gallery to TableViewCell - ios

PostViewController
I'm working on a social app. In which I want to upload photos from gallery to my TableViewCell by UploadButton. By programmatically I created some posts, as shown in MainScreenViewController, like facebook or instagram. But now I want to add a photo by picking up from gallery and upload to the VC.
This is my PostViewController in which I'm getting a photo from library and now I want to post it on MainScreenViewController's ViewControllersTableView.
Kindly help at this point
import UIKit
import Firebase
import FirebaseDatabase
import FirebaseStorage
class PostViewController: UIViewController {
var ref = DatabaseReference.init()
#IBOutlet weak var txtText: UITextField!
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var btnUpload: UIButton!
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
btnUpload.designButton(borderWidth: 0, borderColor: UIColor.clear)
self.ref = Database.database().reference()
let tapGesture = UITapGestureRecognizer()
tapGesture.addTarget(self, action: #selector(PostViewController.openGallery(tapGesture:)))
myImageView.isUserInteractionEnabled = true
myImageView.addGestureRecognizer(tapGesture)
}
func saveFIRData() {
self.uploadImage(self.myImageView.image!) { url in
self.saveImage(name: self.txtText.text!, profileURL: url!) { success in
if success != nil {
print("Good")
}
}
}
}
#objc func openGallery(tapGesture: UITapGestureRecognizer) {
self.setUpImagePicker()
}
#IBAction func btnSaveClick(_ sender: UIButton) {
self.saveFIRData()
}
}
extension PostViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func setUpImagePicker() {
if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.delegate = self
imagePicker.isEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
myImageView.image = image
self.dismiss(animated: true, completion: nil)
}
}
extension PostViewController {
func uploadImage(_ image:UIImage, completion: #escaping ((_ url: URL?) -> ())) {
let storageRef = Storage.storage().reference().child("myImage.png")
let imgData = myImageView.image?.pngData()
let metaData = StorageMetadata()
metaData.contentType = "image/png"
storageRef.putData(imgData!, metadata: metaData) { (metdata, error) in
if error == nil {
print("Success")
storageRef.downloadURL(completion: { (url, error) in
completion(url!)
})
} else {
print("error in save image")
completion(nil)
}
}
}
func saveImage(name: String, profileURL: URL, completion: #escaping ((_ url: URL?) -> ())) {
let dict = ["name": "Omer", "text": txtText.text!, "profileURL": profileURL.absoluteString] as [String: Any]
self.ref.child("chat").childByAutoId().setValue(dict)
}
}
MainScreenViewController
import UIKit
import Firebase
import FirebaseAuth
class MainScreenViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var cardTableView: UITableView!
let pictures: [UIImage] = [UIImage(named: "1")!, UIImage(named: "2")!, UIImage(named: "3")!]
let images: [UIImage] = [UIImage(named: "aa")!, UIImage(named: "ab")!, UIImage(named: "ac")!]
let titles: [String] = ["Che Guevara", "BatMan", "Information Technology"]
let descriptions: [String] = ["Ernesto Che Guevara was an Argentine Marxist revolutionary, guerrilla leader.", "Batman ventures into Gotham City's underworld.", "Information technology is defined as a broad term that includes the development."]
override func viewDidLoad() {
super.viewDidLoad()
cardTableView.delegate = self
cardTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 400
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cardCell", for: indexPath) as! CardCell
cell.configure(picture: pictures[indexPath.row], img: images[indexPath.row], title: titles[indexPath.row], description: descriptions[indexPath.row])
return cell
}
}
CardCell
import UIKit
import Firebase
import FirebaseAuth
class CardCell: UITableViewCell {
#IBOutlet weak var cardView: UIView!
#IBOutlet weak var pictureView: UIImageView!
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var btnLike: UIButton!
// set up the cell
func configure(picture: UIImage, img: UIImage, title: String, description: String) {
pictureView.image = picture
profileImage.image = img
titleLabel.text = title
descriptionLabel.text = description
cardView.layer.shadowColor = UIColor.gray.cgColor
cardView.layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
cardView.layer.shadowOpacity = 1.0
cardView.layer.masksToBounds = false
cardView.layer.cornerRadius = 2.0
}
#IBAction func btnLike_Click(_ sender: UIButton) {
if btnLike.tag == 0 {
btnLike.setImage(UIImage(named: "h1"), for: .normal)
btnLike.tag = 1
} else {
btnLike.setImage(UIImage(named: "h4"), for: .normal)
btnLike.tag = 0
}
}
}

I think you can use closure to pass data back to MainVC which I found it lot easier
Create a closure in PostViewController :
var uploadedImage : ((UIImage)->Void)?
Assign the uploadedImage property before the picker is dismissed
uploadedImage(myUplodedImage)?
Use the property of the closure when you initiate it on MainViewController
let vc = PostViewController(nibName : "something", bundle : nil)
vc.uploadedImage = {
// Add some action here when the PostViewController dismiss
}
navigationController.pushViewController(vc, animated : true)
Additional references :
Passing Data Between View Controllers

Related

Passing data from Table view cell using button delegate

I want to pass the data from one view controller to another view controller when the user clicked the button . I am using button with delegate to pass the table view cell values into different view controller view . In second view controller I have two labels and one image to display the fields but the problem is when I clicked the button it is empty.
Here is the cell code .
import UIKit
protocol CellSubclassDelegate: AnyObject {
func buttonTapped(cell: MovieViewCell)
}
class MovieViewCell: UITableViewCell {
weak var delegate:CellSubclassDelegate?
static let identifier = "MovieViewCell"
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var movieTitle: UILabel!
#IBOutlet weak var movieOverview: UILabel!
#IBOutlet weak var someButton: UIButton!
#IBAction func someButtonTapped(_ sender: UIButton) {
self.delegate?.buttonTapped(cell: self)
}
override func prepareForReuse() {
super.prepareForReuse()
self.delegate = nil
}
func configureCell(title: String?, overview: String?, data: Data?) {
movieTitle.text = title
movieOverview.text = overview
if let imageData = data{
movieImage.image = UIImage(data: imageData)
// movieImage.image = nil
}
}
}
Here is the first view controller code .
import UIKit
class MovieViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
private var presenter: MoviePresenter!
var finalname = ""
var movieTitle = ""
var movieOverview = ""
var movieImage : UIImage?
override func viewDidLoad() {
super.viewDidLoad()
userName.text = "Hello: " + finalname
setUpUI()
presenter = MoviePresenter(view: self)
searchBarText()
}
private func setUpUI() {
tableView.dataSource = self
tableView.delegate = self
}
private func searchBarText() {
searchBar.delegate = self
}
#IBAction func selectSegment(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
setUpUI()
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == ""{
presenter.getMovies()
}
else {
presenter.movies = presenter.movies.filter({ movies in
let originalTitle = movies.originalTitle.lowercased().range(of: searchText.lowercased())
let overview = movies.overview.lowercased().range(of: searchText.lowercased())
let posterPath = movies.posterPath.lowercased().range(of: searchText.lowercased())
return (originalTitle != nil) == true || (overview != nil) == true || (posterPath != nil) == true}
)
}
tableView.reloadData()
}
}
extension MovieViewController: MovieViewProtocol {
func resfreshTableView() {
tableView.reloadData()
}
func displayError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
alert.addAction(doneButton)
present(alert, animated: true, completion: nil)
}
}
extension MovieViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let data = presenter.getImageData(by: row)
cell.delegate = self
cell.configureCell(title: title, overview: overview, data: data)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
dc.imagemovie = UIImage(data: presenter.getImageData(by: row)!)
self.navigationController?.pushViewController(dc, animated: true)
}
}
extension MovieViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
extension MovieViewController : CellSubclassDelegate{
func buttonTapped(cell: MovieViewCell) {
guard (self.tableView.indexPath(for: cell) != nil) else {return}
let customViewController = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as? MovieDeatilsViewController
customViewController?.titlemovie = movieTitle
customViewController?.imagemovie = movieImage
customViewController?.overview = movieOverview
self.navigationController?.pushViewController(customViewController!, animated: true)
}
}
Here is the details view controller code .
class MovieDeatilsViewController: UIViewController {
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var movieTitle: UILabel!
#IBOutlet weak var movieOverview: UILabel!
var titlemovie = ""
var overview = ""
var imagemovie :UIImage?
override func viewDidLoad() {
super.viewDidLoad()
movieTitle.text = titlemovie
movieOverview.text = overview
movieImage.image = imagemovie
}
}
Here is the result when I clicked the button .
The problem is you don't update you're global properties when selecting each of you're row,
If you pass data over cell delegate and pass you're cell through delegate, you can pass data from cell like:
customViewController?.titlemovie = cell.movieTitle.text ?? ""
customViewController?.imagemovie = cell.movieImage.image
customViewController?.overview = cell.movieOverview.text ?? ""
of course it would be better to pass you're data model to you're cell. and then share that through you're delegate not share you're cell, like:
protocol CellSubclassDelegate: AnyObject {
func buttonTapped(cell: MovieModel)
}

Why isn't my Image displaying in the UIImageView?

I want to get an if statement which, if the selected button corresponded to the first image view, set it to the first image, else set it to the second image view... But, once I select the image from the image picker, it just ignores it and moves on like if nothing happened.
Here is my code:
(it down under the image picker controller func...)
class UploadSubPostCell: UICollectionViewCell {
#IBOutlet weak var previewStep: UIImageView!
}
class UploadViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var previewImage: UIImageView!
#IBOutlet weak var postBtn: UIButton!
#IBOutlet weak var selectBtn: UIButton!
#IBOutlet weak var postscollectionview: UICollectionView!
#IBOutlet weak var selectStepsBtn: UIButton!
var picker = UIImagePickerController()
var isThumbnailImage = true
var subpostsArray = [UIImage]()
var subposts = [SubPost]()
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBarItems()
picker.delegate = self
}
func setupNavigationBarItems() {
navigationItem.title = "Upload"
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
let path = IndexPath(item: 0, section: 0)
let cell = self.postscollectionview.cellForItem(at: path) as? UploadSubPostCell
if isThumbnailImage{
previewImage.image = image
} else {
cell?.previewStep.image = image
}
selectBtn.isHidden = false
selectStepsBtn.isHidden = false
postBtn.isHidden = false
if isThumbnailImage == false {
subpostsArray.append(image)
subposts.count + 1
print("Appended image to array:", subpostsArray)
}
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectStepPressed(_ sender: Any) {
picker.allowsEditing = true
picker.sourceType = .photoLibrary
isThumbnailImage = false
self.present(picker, animated: true, completion: nil)
}
#IBAction func selectPressed(_ sender: Any) {
picker.allowsEditing = true
picker.sourceType = .photoLibrary
isThumbnailImage = true
self.present(picker, animated: true, completion: nil)
}
#IBAction func addNewPressed(_ sender: Any) {
}
#IBAction func postPressed(_ sender: Any) {
AppDelegate.instance().showActivityIndicator()
let uid = Auth.auth().currentUser!.uid
let ref = Database.database().reference()
let storage = Storage.storage().reference(forURL: "gs://mobile-d9fcd.appspot.com")
let key = ref.child("posts").childByAutoId().key
let imageRef = storage.child("posts").child(uid).child("\(key).jpg")
let data = UIImageJPEGRepresentation(self.previewImage.image!, 0.6)
let uploadTask = imageRef.putData(data!, metadata: nil) { (metadata, error) in
if error != nil {
print(error!.localizedDescription)
AppDelegate.instance().dismissActivityIndicator()
return
}
imageRef.downloadURL(completion: { (url, error) in
if let url = url {
let feed = ["userID" : uid,
"pathToImage" : url.absoluteString,
"likes" : 0,
"author" : Auth.auth().currentUser!.displayName!,
"postID" : key] as [String : Any]
let postFeed = ["\(key)" : feed]
ref.child("posts").updateChildValues(postFeed)
AppDelegate.instance().dismissActivityIndicator()
self.picker.dismiss(animated: true, completion: nil)
}
})
}
uploadTask.resume()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath) as! PostCell
cell.postImage.downloadImage(from: self.posts[indexPath.row].pathToImage)
cell.authorLabel.text = self.posts[indexPath.row].author
cell.likeLabel.text = "\(self.posts[indexPath.row].likes!) Likes"
cell.postID = self.posts[indexPath.row].postID
}
}
}
In didFinishPickingMediaWithInfo, you should save the image with the dataSource, if you know what cell the image goes into, use tableView.reloadRows(at indexPaths:) to reload the cell. Add the image to cell.previewStep.image in cellForRowAt()
if you are using swift version 4.2 and above, you should be replacing:
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
by:
if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {

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."
}
}

Swift - tableviewcell returns empty using custom cell

I am new to swift programming and would need some help to check what is wrong in my tableviewcell. I have tried alot of great suggestions on stackoverflow.( make sure your outlets are connected, set delegate and datasource of your tableview to self)
This is my ViewController:
import UIKit
class CharacterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate , APIControllerProtocol {
#IBOutlet weak var CharacterInfoView: UITableView!
var apiController:APIController!
var dataArray: [[String:Any]]?
var processcharacter= [CharacterListModel]()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor=UIColor.white
self.CharacterInfoView.estimatedRowHeight = 44
self.CharacterInfoView.rowHeight = UITableViewAutomaticDimension
self.CharacterInfoView.dataSource = self
self.CharacterInfoView.delegate = self
apiController = APIController()
apiController.delegate=self
self.navigationItem.title = "Character"
self.view.showLoading()
apiController.getCharacterData{ (statusCode, data, response, error) -> () in
self.view.stopLoading()
if(statusCode == nil)
{
self.view.showServiceNotAvailableMessage(self)
}
if !(error == nil)
{
self.view.showServiceNotAvailableMessage(self)
}
if statusCode == 200
{
do
{
self.processcharacter= CharacterListData.processData(data: data)
self.CharacterInfoView.reloadData()
}
catch(_ as NSError)
{
}
}
else
{
return
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.dataArray == nil
{
return 0
}
else
{
return processcharacter.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"Cell", for:indexPath) as! CharacterTableViewCell
var processcharacters= processcharacter[indexPath.row]
cell.location.text = processcharacters.location
cell.name.text = processcharacters.name
cell.characterID.text = processcharacters.characterID
cell.time.text = processcharacters.lastlocatedtime
if wecares.sos == true {
processcharacters.imagebutton = UIImage(named: "sos_icon")!
}
else{
processcharacters.imagebutton = UIImage(named: "null_button")!
}
cell.button.image = processcharacters.imagebutton
return cell
}
func reachabilityChanged(_ status: Bool) {
}
}
This is my subclass for my ViewController:
import Foundation
class CharacterListDataHelper: NSObject {
static func processData(data: AnyObject?) -> [CharacterListModel]
{
var modelList:[CharacterListModel] = [CharacterListModel]()
let darr = try? JSONSerialization.jsonObject(with: data! as! Data, options: .mutableLeaves) as! [[String:Any]]
var dataModel:CharacterListModel
for obj in darr!
{
dataModel = CharacterListModel()
dataModel.location = obj["playerLocation"] as! String
dataModel.name = obj["playerName"] as! String
dataModel.characterID= obj["playerID"] as! String
dataModel.lastlocatedtime = obj["lastUpdatedTime"] as! String
}
modelList.append(dataModel)
}
return modelList
}
}
This is my Model:
import Foundation
struct CharacterListModel {
var name: String?
var characterID: String?
var location: String?
var lastlocatedtime: String?
var imagebutton: UIImage?
var sos: Bool?
}
This is my TableViewCell:
class CharacterTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var characterID: UILabel!
#IBOutlet weak var location: UILabel!
#IBOutlet weak var lastlocatedtime: UILabel!
#IBOutlet weak var button: 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
}
Thanks in advance!
Don't fetch data in viewDidLoad() method. Fetch in viewWillAppear and reload after getting data.

Why is my code not working? - setObjectForKey: key cannot be nil

Working on an app where I store data on Firebase and load the data into my TableView. Had to change the (old) code a little from a tutorial i found so I hope someone can spot the mistake i made. The user can add an event with a: name, date, description (all strings) and Image. This data is loaded into the TableView onto three "labels" and and image on top.
import UIKit
import Firebase
import FirebaseDatabaseUI
class EventViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
//outlets for text & image
#IBOutlet weak var photoImageView: UIImageView!
#IBOutlet weak var eventName: UITextField!
#IBOutlet weak var eventDate: UITextField!
#IBOutlet weak var eventDes: UITextView!
//Database connection
let rootref = FIRDatabase().reference()
var imagePicker: UIImagePickerController = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func submitEvent(sender: AnyObject) {
let name = eventName.text
let date = eventDate.text
let text = eventDes.text
var data: NSData = NSData()
if let image = photoImageView.image {
data = UIImageJPEGRepresentation(image,0.1)!
}
let base64String = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let user: NSDictionary = ["name":name!, "date":date!, "text":text!, "photoBase64":base64String]
//Add firebase child node
let event = FIRDatabase().reference().child(name!)
// Write data to Firebase
event.setValue(user)
navigationController?.popViewControllerAnimated(true)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
view.endEditing(true)
super.touchesBegan(touches, withEvent: event)
}
//UIImagePickerControllerDelegate methods
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
imagePicker.dismissViewControllerAnimated(true, completion: nil)
photoImageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func addPicture(sender: AnyObject) {
if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated: true, completion: nil)
} else {
imagePicker.allowsEditing = false
imagePicker.sourceType = .PhotoLibrary
imagePicker.delegate = self
presentViewController(imagePicker, animated: true, completion:nil)
}
}
}
My TABLEVIEWCONTROLLER
import UIKit
import Firebase
class EventTableViewController: UITableViewController {
#IBOutlet weak var eventImage: UIImageView!
#IBOutlet weak var eventName: UILabel!
#IBOutlet weak var eventDate: UILabel!
#IBOutlet weak var eventText: UITextView!
let rootref = FIRDatabase().reference()
var items = [NSDictionary]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
items = [NSDictionary]()
FIRDatabase.load()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//TableView Data
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
configureCell(cell, indexPath: indexPath)
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let dict = items[indexPath.row]
let name = dict["name"] as! String
// delete data from firebase
let event = FIRDatabase().reference().child(name)
event.removeValue()
}
}
// MARK:- Configure Cell
func configureCell(cell: UITableViewCell, indexPath: NSIndexPath) {
let dict = items[indexPath.row]
eventName.text = dict["name"] as? String
eventDate.text = dict["name"] as? String
eventText.text = dict["name"] as? String
let base64String = dict["photoBase64"] as! String
populateImage(cell, imageString: base64String)
}
func populateImage(cell:UITableViewCell, imageString: String) {
let decodedData = NSData(base64EncodedString: imageString, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let decodedImage = UIImage(data: decodedData!)
cell.imageView!.image = decodedImage
}
//load data from Firebase
func loadDataFromFirebase() {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
rootref.observeEventType(.Value, withBlock: { snapshot in
var tempItems = [NSDictionary]()
for item in snapshot.children {
let child = item as! FIRDataSnapshot
let dict = child.value as! NSDictionary
tempItems.append(dict)
}
self.items = tempItems
self.tableView.reloadData()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
})
}
}
IMAGE TO GET AN IDEA OF MY WORK
My app so far
Look at this line:
let user: NSDictionary = ["name":name!, "date":date!, "text":text!, "photoBase64":base64String]
You are doing a lot of forced unwrapping here. In fact, you do a lot of forced unwrapping in several places. When you do this you are saying "I am 100% sure there will ALWAYS be a value here". Avoid this a nearly all costs. That line should look like this.
if let unwrappedName = name , unwrappedDate = date, unwrappedText = text{
let user: NSDictionary = ["name":unwrappedName, "date":unwrappedDate, "text":unwrappedText, "photoBase64":base64String]
}
Unwrapping optionals like this will keep your app from crashing. You should put a breakpoint here to see what value is nil. Every time you use a ! you should think VERY carefully about about it.
I believe the error is where you set up your database connection.
You have: let rootref = FIRDatabase().reference()
It should be:let rootref = FIRDatabase().database().reference()
I was getting the same error and this fixed it for me.

Resources