I have a controller "Feed" which lists multiple posts via a table (a title and image) from Firebase.
On touch of a button, it brings to a "Feed Details" controller, where I would like the data (title image and caption) from the post clicked previously (parent) being display. (see screenshot 2)
At the moment On Click, I just got static information, none of the information are being fetch from Firebase. They are all being called correctly in the main screen, however. So the problem is when it segue to the "DetailsController"
How is it possible to fetch the details from the item click previously ??
Currently this is my feed controller:
//
// Feed.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var linkbutton: UIButton!
#IBOutlet weak var captionInputView: InputTextView!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
feedTableView.dataSource = self
feedTableView.rowHeight = UITableViewAutomaticDimension
feedTableView.estimatedRowHeight = 320
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
self.feedTableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
// here you need to add
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
cell.linkbutton.tag = indexPath.row
cell.linkbutton.addTarget(self, action: #selector(FeedVC.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
}
else
{
fatalError()
}
}
func toFeedDetailAction(_ sender: UIButton) {
let FeedDetailsController = self.storyboard?.instantiateViewController(withIdentifier: "FeedDetailsController") as! FeedDetailsController
FeedDetailsController.post = posts[sender.tag]
self.navigationController?.pushViewController(FeedDetailsController, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
and my Feed Details:
//
// FeedDetails.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedDetailsController: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var linkbutton: UIButton!
#IBOutlet weak var captionInputView: InputTextView!
var post: Post!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
}
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
// here you need to add
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
cell.linkbutton.tag = indexPath.row
cell.linkbutton.addTarget(self, action: #selector(FeedVC.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
}
else
{
fatalError()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
You need to implement prepare(for:sender:) in your table view (a UITableViewDelegate method) and add the data you need to display to your detail view controller there.
Okay, I tried using your code but there are so many missing elements that it was returning errors so I don't know if this will compile but it should do.
First:
I assume the the private var posts = [Post]() in both classes hold the same value of the posts
Second:
you need to add UITableViewDelegate to your superclass
class FeedVC: UIViewController, UITableViewDataSource, UITableViewDelegate, ...
Third:
add the following in your FeedVC:
private var selectedIndexPath: Int = 0
and this in your FeedDetailsController
var selectedIndexPath: Int = 0 // cannot be private to get accessed from FeedVC
and this Delegate function in FeedVC:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
}
and the prepare for segue in FeedVC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let newViewController = segue.destination as! FeedDetailsController
newViewController.selectedIndexPath = selectedIndexPath
}
Lastly you can now use the selectedIndexPath within posts to get the chosen post
let post = posts[selectedIndexPath]
Hope This Helps!
Related
I need help with solving one problem with core data. I have a news app with two view controllers. In the main view controller I'm loading news data in table view in custom cell. Here I have a button, on which should I tap and save news to another view controller. How it looks now: How it looks So when we tap on this blue button, it should save news from the cell and display on second view controller. I have created a core data model like this: Core data model Here is code in my first view controller:
import UIKit
import SafariServices
import CoreData
class ViewController: UIViewController, UISearchBarDelegate, UpdateTableViewDelegate {
#IBOutlet weak var pecodeTableView: UITableView!
private var articles = [News]()
// private var viewModels = [NewsTableViewCellViewModel]()
private var viewModel = NewsListViewModel()
var newsTitle: String?
var newsAuthor: String?
var newsDesc: String?
var urlString: String?
var newsDate: String?
private let searchVC = UISearchController(searchResultsController: nil)
var selectedRow = Int()
override func viewDidLoad() {
super.viewDidLoad()
pecodeTableView.delegate = self
pecodeTableView.dataSource = self
pecodeTableView.register(UINib(nibName: S.CustomCell.customNewsCell, bundle: nil), forCellReuseIdentifier: S.CustomCell.customCellIdentifier)
// fetchAllNews()
viewModel.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
categoryMenu()
loadData()
}
private func loadNewsData(api: String){
let apiService = APIService(categoryCode: api)
apiService.getNewsData {(result) in
switch result{
case .success(let NewsOf):
CoreData.sharedInstance.saveDataOf(news: NewsOf.articles)
case .failure(let error):
print("Error processing json data: \(error)")
}
}
}
func reloadData(sender: NewsListViewModel) {
self.pecodeTableView.reloadData()
}
//MARK: - Networking
private func loadData(){
viewModel.retrieveDataFromCoreData()
}
//MARK: - UIView UImenu
func categoryMenu(){
var categoryAction: UIMenu{
let menuAction = Category.allCases.map { (item) -> UIAction in
let name = item.rawValue
return UIAction(title: name.capitalized, image: UIImage(systemName: item.systemImage)) { [weak self](_) in
self?.loadNewsData(api: name)
self?.loadData()
self?.reloadData(sender: self!.viewModel)
}
}
return UIMenu(title: "Change Category", children: menuAction)
}
let categoryButton = UIBarButtonItem(image: UIImage(systemName: "scroll"), menu: categoryAction)
navigationItem.leftBarButtonItem = categoryButton
}
#IBAction func goToFavouritesNews(_ sender: UIButton) {
performSegue(withIdentifier: S.Segues.goToFav, sender: self)
}
private func createSearchBar() {
navigationItem.searchController = searchVC
searchVC.searchBar.delegate = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRowsInSection(section: section)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: S.CustomCell.customCellIdentifier, for: indexPath) as? CustomNewsCell
let object = viewModel.object(indexPath: indexPath)!
cell?.setCellWithValuesOf(object)
cell?.saveNewsBtn.tag = indexPath.row
cell?.saveNewsBtn.addTarget(self, action: #selector(didTapCellButton(sender:)), for: .touchUpInside)
return cell!
}
#objc func didTapCellButton(sender: FavouritesCell) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "SavedNews", in: context)
let newNewsSave = SavedNews(entity: entity!, insertInto: context)
newNewsSave.author = newsAuthor
newNewsSave.desc = newsDesc
newNewsSave.title = newsTitle
do {
try context.save()
savedNews.append(newNewsSave)
navigationController?.popViewController(animated: true)
} catch {
print("Error saving")
}
print("Done")
}
Also I want to show you my parsing functions and model for this: *APIService.swift*
import Foundation
class APIService{
private var dataTask: URLSessionDataTask?
public let resourceURL: URL
private let API_KEY = "e2a69f7f9567451ba484c85614356c30"
private let host = "https://newsapi.org"
private let headlines = "/v2/top-headlines?"
init(categoryCode: String){
let resourceString = "\(host)\(headlines)country=us&category=\(categoryCode)&apiKey=\(API_KEY)"
print(resourceString)
guard let resourceURL = URL(string: resourceString) else {
fatalError()
}
self.resourceURL = resourceURL
}
//MARK: - Get News
func getNewsData(completion: #escaping (Result<Articles, Error>) -> Void){
dataTask = URLSession.shared.dataTask(with: resourceURL) { (data, response, error) in
if let error = error{
completion(.failure(error))
print("DataTask error: - \(error.localizedDescription)")
}
guard let response = response as? HTTPURLResponse else{
print("Empty Response")
return
}
print("Response status code: - \(response.statusCode)")
guard let data = data else {
print("Empty Data")
return
}
do{
let decoder = JSONDecoder()
let jsonData = try decoder.decode(Articles.self, from: data)
DispatchQueue.main.async {
completion(.success(jsonData))
}
}catch let error{
completion(.failure(error))
}
}
dataTask?.resume()
}
}
And here is NewsModel.swift:
import Foundation
struct Articles: Codable {
let articles: [News]
private enum CodingKeys: String, CodingKey{
case articles = "articles"
}
}
struct News: Codable {
let author: String?
let source: Source
let title: String
let description: String?
let url: URL?
let urlToImage: URL?
let publishedAt: String?
private enum CodingKeys: String, CodingKey{
case author = "author"
case title = "title"
case url = "url"
case urlToImage = "urlToImage"
case publishedAt = "publishedAt"
case description = "description"
case source = "source"
}
}
struct Source: Codable {
let name: String?
}
Here is my code in CustomNewsCell.swift:
import UIKit
protocol CustomNewsDelegate: AnyObject {
func btnFavPress(cell: CustomNewsCell)
}
private var loadImage = LoadToImage()
private var formatDate = FormatDate()
class CustomNewsCell: UITableViewCell {
weak var delegate: CustomNewsDelegate?
#IBOutlet weak var saveNewsBtn: UIButton!
#IBOutlet weak var imageOutlet: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var dateLabel: 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 setCellWithValuesOf(_ news: SavedNews){
updateUI(title: news.title, url: news.url, urlToImage: news.urlToImage, publishedAt: news.publishedAt, author: news.author ?? "No author", description: news.desc, source: news.source)
}
private func updateUI(title: String?, url: URL?, urlToImage: URL?, publishedAt: String?, author: String, description: String?, source: String?){
//title
self.titleLabel.text = title
self.authorLabel.text = author
self.descLabel.text = description
//date
let dateString = formatDate.formatDate(from: publishedAt ?? "")
let date = formatDate.formatDateString(from: dateString)
self.dateLabel.text = date
//image
guard let urlToImageString = urlToImage else {return}
imageOutlet.image = nil
loadImage.getImageDataFrom(url: urlToImageString) { [weak self] data in
guard let data = data, let image = UIImage(data: data) else{
DispatchQueue.main.async {
self?.imageOutlet.image = UIImage(named: "noImage")
}
return
}
self?.imageOutlet.image = image
}
}
#IBAction func saveBtnPressed(_ sender: UIButton) {
delegate?.btnFavPress(cell: self)
}
}
First time I've tried with delegate method for this blue button, but now as you can see I've created a selector method. Maybe it's not correct and need to fix it. Here is the code in the second view controller, which should show saved news from first view controller:
import UIKit
import CoreData
var savedNews = [SavedNews]()
class FavouriteNewsViewController: UIViewController {
#IBOutlet weak var favTableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var result: [SavedNews] = []
var newsTitleNew: String?
var newsDescNew: String?
var newsAuthor: String?
override func viewDidLoad() {
super.viewDidLoad()
favTableView.delegate = self
favTableView.delegate = self
fetch()
// loadSavedNews()
favTableView.register(UINib(nibName: S.FavouriteCell.favouriteCell, bundle: nil), forCellReuseIdentifier: S.FavouriteCell.favouriteCellIdentifier)
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// fetch()
favTableView.reloadData()
}
#IBAction func goToNewsFeed(_ sender: UIButton) {
performSegue(withIdentifier: S.Segues.goToNewsFeed, sender: self)
}
}
extension FavouriteNewsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return savedNews.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = favTableView.dequeueReusableCell(withIdentifier: S.FavouriteCell.favouriteCellIdentifier, for: indexPath) as! FavouritesCell
let newRslt: SavedNews!
newRslt = savedNews[indexPath.row]
cell.favAuthor.text = newRslt.author
cell.favDesc.text = newRslt.desc
cell.favTitle.text = newRslt.title
return cell
}
func fetch() {
let request = NSFetchRequest<SavedNews>(entityName: "SavedNews")
do {
savedNews = try context.fetch(request)
} catch {
print(error)
}
}
}
And code for cell for this controller:
import UIKit
import CoreData
class FavouritesCell: UITableViewCell {
#IBOutlet weak var favImage: UIImageView!
#IBOutlet weak var favTitle: UILabel!
#IBOutlet weak var favDesc: UILabel!
#IBOutlet weak var favAuthor: 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
}
}
If somebody can help with this, it will be amazing. Because I really don't how to do this. Thank you!
In firebase, I have a storage with PDF's and a collection that has a reference to the stored PDF, along with the name of the PDF.
I'm retrieving the collection documents and display the headline in a tableview.
I want to be able to click the tableview cell, then display the pdf in a webkit view in another viewcontroller.
How do I fetch the PDF in storage and display it in webkit view?
My code so far:
Handler
class PDFHandler {
static let db = Firestore.firestore()
static let storage = Storage.storage()
static var list = [PDF]()
static var downloadURL : String?
static func Create(title: String, url: String){
}
static func startListener(tableView: UITableView){
print("Listening has begun")
db.collection("PDF").addSnapshotListener { (snap, error) in
if error == nil{
self.list.removeAll()
for pdf in snap!.documents{
let map = pdf.data()
let head = map["Headline"] as! String
let url = map["URL"] as? String ?? "empty"
let newPDF = PDF(id: pdf.documentID, headline: head, url: url)
print(newPDF)
self.list.append(newPDF)
}
DispatchQueue.main.async {
tableView.reloadData()
}
}
}
}
static func downloadPdfs(pdfID: String, pdfurl: String){
print("Download initiated")
let pdfRef = storage.reference(withPath: pdfID)
pdfRef.getData(maxSize: 99999999999) { (data, error) in
if error == nil{
print("Success downloading PDF")
DispatchQueue.main.async {
}
}else{
print("Error fetching pdf")
}
}
}
static func getSize() -> Int{
return list.count
}
static func getPDFat(index: Int) -> PDF{
return list[index]
}
}
Tableview
class ReportViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
PDFHandler.startListener(tableView: tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return PDFHandler.getSize()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PDFCell")
cell?.textLabel?.text = PDFHandler.getPDFat(index: indexPath.row).headline
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PDFViewController{
destination.rowNumber = tableView.indexPathForSelectedRow!.row
}
}
}
PDFView VC
import UIKit
import WebKit
class PDFViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var pdfview: WKWebView!
var rowNumber = 0
var url = ""
override func viewDidLoad() {
super.viewDidLoad()
let myUrl = URL(string: "")
let myRequest = URLRequest(url: myUrl!)
pdfview.load(myRequest)
}
override func loadView() {
let webConfig = WKWebViewConfiguration()
pdfview = WKWebView(frame: .zero, configuration: webConfig)
pdfview.uiDelegate = self
view = pdfview
}
}
In Swift xcode not able to call the delegate function though defined correctly.
Using it to get the value of active cell.
button declaration file
import UIKit
protocol CellDelegate : class {
func didClickPlayButton(_ sender: VideoTableViewCell)
}
class VideoTableViewCell: UITableViewCell {
#IBOutlet weak var cellView: UIView!
#IBOutlet weak var VideoThumbnail : UIImageView!
#IBOutlet weak var VideoName: UILabel!
#IBOutlet weak var uploadDate: UILabel!
#IBOutlet weak var playButton: UIButton!
weak var Delegate: CellDelegate?
#IBAction func pressedPlay(_ sender: UIButton) {
Delegate?.didClickPlayButton(self)
print("PLAY")
}
}
Function declaration file
import UIKit
import FirebaseAuth
import FirebaseDatabase
import AVFoundation
import AVKit
class VideoVC: UIViewController,UITableViewDelegate,UITableViewDataSource,CellDelegate {
let avPlayerViewController = AVPlayerViewController()
var avPlayer : AVPlayer?
func didClickPlayButton(_ sender : VideoTableViewCell) {
let indexPath = self.tableview.indexPath(for: sender)
print("Play Button Pressed")
if let index = indexPath?.row
{
let movieUrl : NSURL? = NSURL(string : posts[(index)].noticeURL)
if let url = movieUrl
{
self.avPlayer = AVPlayer(url: url as URL)
self.avPlayerViewController.player = self.avPlayer
self.present(self.avPlayerViewController, animated: true, completion: {
self.avPlayerViewController.player?.play()
})
}
}
}
#IBOutlet weak var tableview: UITableView!
var posts = [Post]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "Videocell") as! VideoTableViewCell
cell.selectionStyle = .none
cell.uploadDate?.text = posts[indexPath.row].uploadDate
cell.VideoName?.text = posts[indexPath.row].noticeName
cell.VideoThumbnail?.image = thumbnail(sourceURL: (URL(string : posts[indexPath.row].noticeURL)!))
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
self.tableview.rowHeight = 300
loadposts()
}
func loadposts()
{
Database.database().reference().child("Videos").observe(.childAdded) { (snapshot : DataSnapshot) in
if let dict = snapshot.value as? [String : Any]
{
let uploaddate = dict["Date"] as! String
let noticename = dict["Name"] as! String
let ImageUrl = dict["URL"] as! String
let post = Post(uploadedDate: uploaddate, nameofnotice: noticename, URLofnotice: ImageUrl)
self.posts.append(post)
self.tableview.reloadData()
}
}
}
func thumbnail(sourceURL:URL) -> UIImage {
let asset = AVAsset(url: sourceURL)
let imageGenerator = AVAssetImageGenerator(asset: asset)
let time = CMTime(seconds: 1, preferredTimescale: 1)
let imageRef = try? imageGenerator.copyCGImage(at: time, actualTime: nil)
if imageRef != nil
{
let uiimage = UIImage(cgImage: imageRef!)
return uiimage
}
else
{
return #imageLiteral(resourceName: "BG_DDIT")
}
}
}
In cellForRow
cell.Delegate = self
and implement the delegate method inside PdfVC
func didClickPlayButton(_ sender: VideoTableViewCell){----}
I have a NSMutableArray and I'm trying to create a search bar. I have been looking for hours and I can't find anything that really helps me because I'm using the NSMutableArray with Firebase. I'm kinda new to Swift. I have created a table view and a search bar. The table view is showing data from the NSMutableArray. How can I make it so when the user is searching on the search bar then the whole post comes up?
If there is a post that contains 2 Strings and an image and one String is "A City" and the other String is the title of the post, and let's say that when the user is searching only for the title of the post or only the city, then the entire post shows. If the result could be on the same viewcontroller like on the same tableview.
I have written this so far in my viewDidLoad:
func setUpSearchBar() {
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
searchController.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
}
And my array is like this:
var posts = NSMutableArray()
The only thing really that I have written is so that the search bar is setup but not really functional because I have not found anything that could help me and since I'm new to this is not either sure how to make the search bar work at all. All the tutorials I have to find so far is very normal arrays like:
var post = ["pear", "orange", "apple"]
But I cant find on how to make the search iterate through my post NSMutableArray.
I would be really happy if anyone could possibly help me through this. I don't want straight answers if possible with some explanation so I can learn this and create this myself.
this is the code that downloads the data and displays it in the TableView
import UIKit
import FirebaseStorage
import FirebaseDatabase
import FirebaseAuth
import FirebaseCore
import Firebase
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var postsTableView: UITableView!
var posts = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchBar()
loadData()
self.postsTableView.delegate = self
self.postsTableView.dataSource = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadData() {
Database.database().reference().child("posts").queryOrdered(byChild: "timeorder").observeSingleEvent(of: .value) { (snapshot) in
if let postsDictionary = snapshot.value as? [String: AnyObject] {
for post in postsDictionary {
self.posts.add(post.value)
}
self.postsTableView.reloadData()
}
}
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PostTableViewCell
// Configure the cell...
let post = self.posts[indexPath.row] as! [String: AnyObject]
cell.titleLabel.text = post["title"] as? String
cell.contentTextView.text = post["content"] as? String
cell.dateAndTimeLabel.text = post["time"] as? String
cell.usernameLabel.text = post["username"] as? String
cell.locationAdressLabel.text = post["adress"] as? String
if let imageName = post["image"] as? String {
let imageRef = Storage.storage().reference().child("images/\(imageName)")
imageRef.getData(maxSize: 25 * 1024 * 1024) { (data, error) -> Void in
if error == nil {
//successfull
let downloadedImage = UIImage(data: data!)
cell.postsImageView.image = downloadedImage
}else {
// error
print("there was an error downloading image: \(String(describing: error?.localizedDescription))")
}
}
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 500.0
}
#IBAction func goToProfilePage(_ sender: Any) {
let logoutSuccess = self.storyboard?.instantiateViewController(withIdentifier: "ProfileVC")
self.present(logoutSuccess!, animated: true, completion: nil)
}
#IBAction func navigateButton(_ sender: Any) {
let navigate = self.storyboard?.instantiateViewController(withIdentifier: "NavigateVC")
self.present(navigate!, animated: true, completion: nil)
}
#IBAction func chooseCountry(_ sender: Any) {
let navigate = self.storyboard?.instantiateViewController(withIdentifier: "CountryVC")
self.present(navigate!, animated: true, completion: nil)
}
func setUpSearchBar() {
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
searchController.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
}
And Here is The Code That uploads the post to Firebase
import UIKit
import FirebaseDatabase
import FirebaseStorage
import FirebaseAuth
import Firebase
class PostViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var addImageButton: UIBarButtonItem!
#IBOutlet weak var pickCountryPicker: UIPickerView!
#IBOutlet weak var choosenCountryLabel: UILabel!
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var locationAdressTextField: UITextField!
var imageFileName = ""
var timeStamps = ""
var secondTimeStamps = ""
override func viewDidLoad() {
super.viewDidLoad()
let currentDateTime = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
timeStamps = "\(dateFormatter.string(from: currentDateTime))"
let secondDateFormatter = DateFormatter()
secondDateFormatter.dateFormat = "yyyy-MM-dd HH:00:00"
secondTimeStamps = "\(secondDateFormatter.string(from: currentDateTime))"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func postButton(_ sender: Any) {
if (self.imageFileName != "") {
if choosenCountryLabel.text == "Afghanistan" {
// image has finshed the uploading, Saving Post!!!
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if let userDictionary = snapshot.value as? [String: AnyObject] {
for user in userDictionary{
if let username = user.value as? String {
if let streetAdress = self.locationAdressTextField.text {
if let title = self.titleTextField.text {
if let content = self.contentTextView.text {
let postObject: Dictionary<String, Any> = [
"uid" : uid,
"title" : title,
"content" : content,
"username" : username,
"time" : self.timeStamps,
"timeorder" : self.secondTimeStamps,
"image" : self.imageFileName,
"adress" : streetAdress
]
Database.database().reference().child("posts").childByAutoId().setValue(postObject)
Database.database().reference().child("Afghanistanposts").childByAutoId().setValue(postObject)
Database.database().reference().child(uid).childByAutoId().setValue(postObject)
let alertPosting = UIAlertController(title: "Successfull upload", message: "Your acty was successfully uploaded.", preferredStyle: .alert)
alertPosting.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "AfghanistanVC")
self.present(vc!, animated: true, completion: nil)
}))
self.present(alertPosting, animated: true, completion: nil)
print("Posted Succesfully to Firebase, Saving Post!!!")
}
}
}
}
}
}
})
}
}
}else{
let alertNotPosting = UIAlertController(title: "Seems like you got connection problems", message: "Your image has not been uploaded. Please Wait 10 seconds and try again.", preferredStyle: .alert)
alertNotPosting.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertNotPosting, animated: true, completion: nil)
}
#IBAction func addImage(_ sender: Any) {
if addImageButton.isEnabled == false {
let alertNotPosting = UIAlertController(title: "Image already Picked", message: "sorry you already have an image picked", preferredStyle: .alert)
alertNotPosting.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertNotPosting, animated: true, completion: nil)
}else {
let picker = UIImagePickerController()
picker.delegate = self
self.present(picker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
self.imageView.image = pickedImage
uploadImage(image: pickedImage)
picker.dismiss(animated: true, completion: nil)
addImageButton.isEnabled = false
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
func uploadImage(image: UIImage){
let randomName = randomStringWithLength(length: 10)
let imageData = UIImageJPEGRepresentation(image, 1.0)
let uploadRef = Storage.storage().reference().child("images/\(randomName).jpg")
let uploadTask = uploadRef.putData(imageData!, metadata: nil) { metadata, error in
if error == nil {
//success
print("SuccessFully uploaded")
self.imageFileName = "\(randomName as String).jpg"
}else {
// not success = error
print("error with the Image: \(String(describing: error?.localizedDescription))")
}
}
}
func randomStringWithLength(length: Int) -> NSString {
let characters: NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var randomString: NSMutableString = NSMutableString(capacity: length)
for i in 0..<length {
var len = UInt32(characters.length)
var rand = arc4random_uniform(len)
randomString.appendFormat("%C", characters.character(at: Int(rand)))
}
return randomString
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return countries[row]
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return countries.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
choosenCountryLabel.text = countries[row]
}
}
and below this is a array full of countries for the picker and is not nessecary to show.
Why not just cast NSMutableArray to a Swift array? For safety I recommend using a guard statement like so:
guard let swiftArrayPosts = posts as? [String] else { return }
Then you can use Swift higher-order functions like filter to find the posts you want:
let filteredPosts = swiftArrayPosts.filter { $0 == "MyQuery" }
Then assign filteredPosts to whatever data source you are using to display them.
I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.