Swift: Show data from tableView to another ViewController (JSON, Alamorife, AlamofireImage) - ios

I'm trying to do an app in which the data were obtained from JSON.
In the picture below you can see the project:
Project
If we click on the photo opens the details page. The problem is because I do not know how to pick up the data shown in the details page. Please help me.
Here is the code
import UIKit
import Alamofire
import AlamofireImage
import SwiftyJSON
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
#IBOutlet weak var searchbarValue: UISearchBar!
weak open var delegate: UISearchBarDelegate?
#IBOutlet weak var tableView: UITableView!
var albumArray = [AnyObject]()
var url = ("https://jsonplaceholder.typicode.com/photos")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.searchbarValue?.delegate = self
Alamofire.request("https://jsonplaceholder.typicode.com/photos").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar[].arrayObject {
self.albumArray = resData as [AnyObject]; ()
}
if self.albumArray.count > 0 {
self.tableView.reloadData()
}
}
}
}
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("https://jsonplaceholder.typicode.com/photos").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar[].arrayObject {
self.albumArray = resData as [AnyObject]; ()
}
if self.albumArray.count > 0 {
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return albumArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CostumTableViewCell
let title = albumArray[indexPath.row]
cell?.titleLabel?.text = title["title"] as? String
//cell?.url?.image = UIImage(data: title as! Data)
let imageUrl = title["thumbnailUrl"] as? String
//print(imageUrl)
let urlRequest = URLRequest(url: URL(string: imageUrl!)!)
Alamofire.request(urlRequest).responseImage { response in
if let image = response.result.value {
// print("image downloaded: \(title["url"])")
cell?.url?.image = image
}
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow?.row
let vc = segue.destination as! DetailsViewController
//here should be the code
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Also you can see the DetailsViewController code:
import UIKit
class DetailsViewController: UIViewController {
var image2 = UIImage()
var title2 = String()
#IBOutlet var mainImageView: UIImageView!
#IBOutlet var songTitle: UILabel!
override func viewDidLoad() {
songTitle.text = title2
mainImageView.image = image2
}
}

You can easily pass value from tableview to detail view using the below code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow
let cell : CostumTableViewCell = self.tableView.cellForRow(at: indexPath!) as! CostumTableViewCell
let vc = segue.destination as! DetailsViewController
vc.image2 = cell.url.image!
vc.title2 = cell.titleLabel.text!
}

class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
var customArr = [CustomElement]()
var arr = [Any]()
// In viewDidLoad , you can append element to customArr
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var image = customArr[indexpath.row].image
var title = customArr[indexpath.row].title
arr.append(image)
arr.append(title)
performSegue(withIdentifier: "showDetails", sender: arr)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow?.row
if segue.identifier = "showDetails" {
if let vc = segue.destination as! DetailsViewController {
vc.arr = sender
}
}
//here should be the code
}
}
class DetailsViewController: UIViewController {
var arr = [Any]()
}

Related

Parse from TableVC to textView in other viewController swift

I am new in programming, and have problem. I am using parse for my array in tableview. When the row is selected i want to segue on another view controller to textView. The tableview works good but i can't get text to textView.
tableVC:
import UIKit
import Parse
class ThirdTableVC: UITableViewController {
#IBOutlet weak var refresherQuotes: UIRefreshControl!
#IBOutlet var quoteTable: UITableView!
var selectedQuote: PFObject?
var quoteItems = [PFObject]()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func updateQuotesResults(_ sender: UIRefreshControl) {
fetchQuotesData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadQuoteTexts(selectedQuote: selectedQuote)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return quoteItems.count
}
func fetchQuotesData() {
let quotesQuery = PFQuery(className: "TotalTest")
quotesQuery.whereKey("Subcategory", equalTo: selectedQuote ?? nil)
quotesQuery.findObjectsInBackground { (objects, error) in
if let realCategoryObjects = objects {
self.quoteItems = realCategoryObjects
self.tableView.reloadData()
self.refresherQuotes.endRefreshing()
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let quoteCell = tableView.dequeueReusableCell(withIdentifier: "quoteCell", for: indexPath)
let quoteItem = quoteItems[indexPath.row]
let quoteUserTitle = quoteItem.object(forKey: "TextQuote") as? String
quoteCell.textLabel?.text = quoteUserTitle
return quoteCell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showQuoteDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let quoteobject = quoteItems[indexPath.row] as? NSDate
let quoteController = (segue.destination as! UINavigationController).topViewController as! DetailViewController
quoteController.detailItem = quoteobject
quoteController.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
quoteController.navigationItem.leftItemsSupplementBackButton = true
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row tapped: \(indexPath.row)")
let selectedQuotes: PFObject = quoteItems[indexPath.row]
let FourthVC = self.storyboard?.instantiateViewController(withIdentifier: "FourthViewController") as! FourthViewController
FourthVC.fourthTextView.text = quoteItems[indexPath.row] as? String
self.navigationController?.pushViewController(FourthVC, animated: true)
}
func loadQuoteTexts(selectedQuote: PFObject!) {
let quoteQuery = PFQuery(className: "TotalQuote")
quoteQuery.whereKey("QuoteSubs", equalTo: selectedQuote ?? nil)
quoteQuery.includeKey("QuoteSubs")
quoteQuery.findObjectsInBackground { (result: [PFObject]?, error) in
if let searchQuoteResults = result {
self.quoteItems = searchQuoteResults
self.quoteTable.reloadData()
}
}
}
}
How can I change this?
viewController with textView:
import UIKit
import Parse
class FourthViewController: UIViewController {
var getQuote: PFObject?
#IBOutlet weak var fourthTextView: UITextView!
#IBOutlet weak var fourthLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
fourthLabel.text! = getQuote as! String
fourthTextView.text! = getQuote as! String
}
}
Please help me to passing texts
If you use pushViewController do it like that , in did selectRowAt
let MainStory: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let desVC = MainStory.instantiateViewController(withIdentifier: "FourthViewController") as! FourthViewController
and now pass your text
desVC.getText = "here goes your text u want to pass"
FourthViewController
set up your var
var getText = String()
so you can finally use
self.navigationController?.pushViewController(desVC, animated: true)
so it will pass all parameters you previous add with desVC.getSomething
in FourthViewController you just need to use getText.
The problem is that you are changing from a view to another with pushViewController, by doing that your prepareForSegue won't be executed.
On your didSelectRow you need to call performSegue(withIdentifier:sender:).
You can lookup this question for more information on how to do it.

Error while passing data between two ViewControllers

I get the following error after passing data from one ViewController to another.
no data
eventStruct(title: What are we gonna do today?, date: 2017-07-13, location: House3, description: Hello beautiful people!)
2017-06-15 22:01:05.697 RUC-App 1.0[48760:5792168] Warning: Attempt to present <RUC_App_1_0.EventViewController: 0x7fb686636460> on <RUC_App_1_0.NewsfeedViewController: 0x7fb68662e6a0> whose view is not in the window hierarchy!
The code from the first ViewController:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class NewsfeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var ref:DatabaseReference!,
posts = [eventStruct]()
#IBOutlet weak var tableview: UITableView!
var propertStruct : (Any)? = nil
override func viewDidLoad() {
super.viewDidLoad()
loadNews()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadNews() {
ref = Database.database().reference()
ref.child("events").queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
if let valueDictionary = snapshot.value as? [AnyHashable:String]
{
let title = valueDictionary["Title"]
let location = valueDictionary["Location"]
let date = valueDictionary["Date"]
let description = valueDictionary["Description"]
self.posts.insert(eventStruct(title: title, date: date, location: location, description: description), at: 0)
self.tableview.reloadData()
}
})
}
///////////////////////// Table View Content \\\\\\\\\\\\\\\\\\\\\\\\\\
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 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let label1 = cell.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
let label2 = cell.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].location
let label3 = cell.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].date
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetails"){
let secondViewController = segue.destination as? EventViewController
secondViewController?.data = sender as? eventStruct
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: posts[indexPath.row])
}
/////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
}
struct eventStruct {
let title: String!
let date: String!
let location: String!
let description: String!
}
The code of the second ViewController:
import UIKit
class EventViewController: UIViewController {
var data: eventStruct?
#IBOutlet weak var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print (data ?? "no data")
self.titleLabel.text = self.data?.title
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You should not have both triggered and manual segues happening at the same time.
Try this:
Delete your didSelectRowAt function (or comment it out), and replace prepare() with:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetails"){
let secondViewController = segue.destination as? EventViewController
if let idx = tableView.indexPathForSelectedRow {
secondViewController?.data = posts[idx.row]
}
}
}

Passing data with segue in the tableViewCell

I want to passing data with segue in the tableViewCell,from BulletinBoadrViewController to BbDetailViewController
class BulletinBoadrViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var bulletinBoards = [BulletinBoard]()
override func viewDidLoad() {
super.viewDidLoad()
bulletinBoards = BulletinBoard.downloadAllBulletinBoard()
self.tableView.reloadData()
tableView.estimatedRowHeight = tableView.rowHeight
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorStyle = .none
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bulletinBoards.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BulletinBoardTableViewCell
let bulletinBoard = bulletinBoards[indexPath.row]
cell.bulletinBoard = bulletinBoard
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "gotodetail", sender: indexPath)
print("Row \(indexPath.row)selected")
}
func prepareForSegue(segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "gotodetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destVC = segue.destination as! BdDeatilViewController
let new = bulletinBoards[indexPath.row]
destVC.bulletinBoard = new
}
}
}
and it's BdDeailViewController
class BdDeatilViewController:UIViewController {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var contentLabel: UITextView!
#IBAction func backtobb(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
var x = [BulletinBoard]()
var bulletinBoard : BulletinBoard!{
didSet{
self.updateUI()
}
}
func updateUI() {
timeLabel.text = bulletinBoard.time
titleLabel.text = bulletinBoard.title
contentLabel.text = bulletinBoard.content
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
and tableViewCell's data is taking from local json file, it's BulletinBoard code
class BulletinBoard {
var title:String?
var time:String?
var content:String?
init(title:String,time:String,content:String) {
self.title = title
self.time = time
self.content = content
}
init(bulletinBoardDictionary:[String:Any]) {
self.title = bulletinBoardDictionary["title"] as? String
self.time = bulletinBoardDictionary["time"] as? String
self.content = bulletinBoardDictionary["content"] as? String
}
static func downloadAllBulletinBoard() -> [BulletinBoard] {
var bulletinBoards = [BulletinBoard]()
//get the json data from the file
let jsonFile = Bundle.main.path(forResource: "BulletinBoardData", ofType: "json")
let jsonFileURL = URL(fileURLWithPath: jsonFile!)
let jsonData = try? Data(contentsOf: jsonFileURL)
//turn the json data into foundation objects (bulletinBoards)
if let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let bulletinBoardDictionaries = jsonDictionary["BulletinBoard"] as! [[String:Any]]
for bulletinBoardDictionary in bulletinBoardDictionaries {
let newBulletinBoard = BulletinBoard(bulletinBoardDictionary: bulletinBoardDictionary)
bulletinBoards.append(newBulletinBoard)
}
}
return bulletinBoards
}
}
Finally,it's my StoryBoard
https://i.stack.imgur.com/JcgdH.png1
Can anyone solve my problem?Thanks!
I think you should retrieve indexPath from sender in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "gotodetail" {
if let indexPath = sender as? IndexPath {
let destVC = segue.destination as! BdDeatilViewController
let new = bulletinBoards[indexPath.row]
destVC.bulletinBoard = new
}
}
}
and update the UI in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.updateUI()
}

Swift prepareForSegue object is nil

I'm trying to pass the event Object into DetailViewController, but it crashes and says: "fatal error: unexpectedly found nil while unwrapping an Optional value". I'm positive the error is in my prepare(for segue:) function, but I've been at this for hours and I can't find out how to fix it. Any help would be appreciated. Thanks!
Here is my code:
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var tableView: UITableView!
var events = [Event]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
parseCSV()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func logOutAction(sender: AnyObject) {
if FIRAuth.auth()?.currentUser != nil {
do {
try FIRAuth.auth()?.signOut()
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SignUp")
present(vc, animated: true, completion: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
func parseCSV() {
let path = Bundle.main.path(forResource: "eventData", ofType: "csv")
do {
let csv = try CSV(contentsOfURL: path!)
let rows = csv.rows
for row in rows {
let eventTitle = row["Title "]!
let eventLoc = row[" Location "]!
let eventStart = row[" Start_Time "]!
let eventEnd = row [" End_Time"]!
let event = Event(title: eventTitle, loc: eventLoc, start: eventStart, end: eventEnd)
events.append(event)
}
} catch let err as NSError {
print(err.debugDescription)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as? EventCell{
let event = events[indexPath.row]
cell.updateUI(event: event)
return cell
} else {
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let event = events[indexPath.row]
performSegue(withIdentifier: "detailSegue", sender: event)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSeque" {
if let detailVC = segue.destination as? DetailViewController {
if let event = sender as? Event {
detailVC.event = event
}
}
}
}
}
My DetailViewController class:
class DetailViewController: UIViewController {
var event: Event!
#IBOutlet weak var eventTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
eventTitle.text = event.title // error here. says event is nil.
}
}
Since the segue is connected from your cell, it already performed automatically when you tap the cell. Performing it programmatically is resulting in your segue being performed twice: once with the value passed correctly and once without.
You can either change your segue to be connected from the view controller instead of the cell in which case your current code would work, or update your code to do all of the logic in prepareForSegue and remove your implementation of didSelectRowAt:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSeque" {
if let detailVC = segue.destination as? DetailViewController {
let row = tableView.indexPathForSelectedRow!.row
detailVC.event = events[row]
}
}
}

Loading wrong url in UIWebView from sidebar menu

I am trying to load an url in UIWebView based on selected row in sidebar menu. For menu I am using SWRevealViewController and I am loading the menu with JSON using Alamofire and SwiftyJSON. It has 3 rows and an url for each row. My code for menu looks like this:
import UIKit
import Alamofire
class MenuTableViewController: UITableViewController {
var valueToPass = String()
var jsonArray = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "my-JSON-URL").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["items"].arrayObject {
self.jsonArray = resData as! [[String:AnyObject]]
}
if self.jsonArray.count > 0 {
self.tableView.reloadData()
}
}
print(self.jsonArray)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (jsonArray.count)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DynamicCell", forIndexPath: indexPath)
let array = jsonArray[indexPath.row]
cell.textLabel?.text = array["name"] as? String
return cell
}
And I segue from this View Controller like this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow!.row
var array = jsonArray[indexPath]
let url = array["url"] as? String
valueToPass = url!
print(valueToPass)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "showDetail") {
let viewController = segue.destinationViewController as! UINavigationController
let targetController = viewController.topViewController as! DetailViewController
targetController.passedValue = valueToPass
}
}
And this is my code in DetailViewController:
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
#IBOutlet weak var menuButton: UIBarButtonItem!
var passedValue = String()
override func viewDidLoad() {
super.viewDidLoad()
if revealViewController() != nil {
menuButton.target = revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(revealViewController().panGestureRecognizer())
}
let url = NSURL (string: passedValue)
let requestObj = NSURLRequest(URL: url!)
webView.loadRequest(requestObj)
}
}
The problem is that when I select any row in menu for the first time, DetailViewController will appear blank. When i select any row second time it gives me the first url, third time - second url and so on. It is not based on which row I select, although the print(valueToPass) in didSelectRowAtIndexPath in MenuTableViewController prints right urls for each cell.
It is because alamofire is Asynchronous and loads in the background thread while your webView is not loaded at all. Hence the empty page for the first time and so on. I suggest you write the Request in another function with a completion handler and call it in viewDidLoad() like so:
func loadURL(url: String, completion: (isDone: Bool) -> ()){
Alamofire.request(.GET, url)...
// code
if self.jsonArray.count > 0 {
self.tableView.reloadData()
}
}
completion(isDone: true)
print(self.jsonArray)
}
}
And then in viewDidLoad() call it like:
override func viewDidLoad() {
super.viewDidLoad()
loadUrl("Your Url here") { success in
if success{
self.tableView.reloadData()
}
else{
}
}
Should fix your problem. Try! :)

Resources