My situation consists of a UITable which when a row is selected it opens up a new VC and sends across a couple of variables.
My problem is that the segue is getting executed before the function DidSelectAtRow is run.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print ("hello")
self.selectedCellTitle = self.communities[indexPath.row]
self.cellId = indexPath.row
print ("now here: ", communityIds[cellId!])
self.performSegue(withIdentifier: "showCommunitySegue", sender: self)
}
I know this because the print commands above are not being executed. The app then crashes out when the next screen because the variables it expected to be there (cellId) are null.
If I delete the segue in storyboard and run then all the debug outputs in that function run correctly. As soon as I create the segue again the app switches to the new screen and crashes before any of that above code is run.
To create my segue I am:
1) right clicking on the cell in my UITableView on VC1 within Storyboard and dragging to my VC2
2) selecting type as show
3) copying the segue identifier name from the prepare for segue function in VC 1, and pasting it into the identifier attribute in Storyboard for the new segue.
Any ideas?
Below is the full code for VC1:
import UIKit
class CommunitiesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var selectedCellTitle: String?
var cellId: Int?
var communities = [String]() //{ didSet { communitiesTableView.reloadData()
// }
// }
var communityIds = [String]()
var flag = true
var userEmailText: String?
var tempComId: Int?
#IBOutlet weak var joinCommunityButton: UIButton!
#IBOutlet weak var createCommunityButton: UIButton!
#IBOutlet weak var communitiesTableView: UITableView!
override func viewDidLoad() {
self.communitiesTableView.delegate = self
self.communitiesTableView.dataSource = self
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.communities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
cell.textLabel?.text = self.communities[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print ("hello")
self.selectedCellTitle = self.communities[indexPath.row]
self.cellId = indexPath.row
print ("now here: ", communityIds[cellId!])
self.performSegue(withIdentifier: "showCommunitySegue", sender: self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if flag == true
{
self.communitiesTableView.reloadData()
let myUrl = URL(string: "http://www.quasisquest.uk/KeepScore/getDetails.php?");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "email=\(self.userEmailText!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
if error != nil {
print("error=\(error)")
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
if let arr = json?["communities"] as? [[String:String]] {
self.communities = arr.flatMap { $0["name"]!}
self.communitiesTableView.reloadData()
}
} catch{
print(error)
}
}
}
task.resume()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "createCommunitySegue" {
let createCommunityController: CreateNewCommunity = segue.destination as! CreateNewCommunity
createCommunityController.myEmail = self.userEmailText
}
if segue.identifier == "joinCommunitySegue" {
let joinCommunityController: JoinCommunity = segue.destination as! JoinCommunity
joinCommunityController.myEmail = self.userEmailText
}
if segue.identifier == "showCommunitySegue" {
let showCommunityController: ShowCommunityViewController = segue.destination as!ShowCommunityViewController
print("yes here: ", self.cellId!)
showCommunityController.communityIsCalled = self.selectedCellTitle
showCommunityController.comIds = self.communityIds
showCommunityController.communityId = self.cellId
}
}
}
You are creating your segue from Cell to ViewController, you need to create segue from ViewController to ViewController like this,
Related
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]()
}
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.
I have a UITableView where data is loaded from a database, a JSON. How do I get this when I select a line, which is taken in another view?
The automarke is to be selected in the tableview and displayed in the label of the other view.
class AutoMarkeTableView: UITableViewController {
var items = [[String:AnyObject]]()
#IBOutlet var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "URL_LINK")!
let urlSession = URLSession.shared
let task = urlSession.dataTask(with: url) { (data, response, error) in
// JSON parsen und Ergebnis in eine Liste von assoziativen Arrays wandeln
let jsonData = try! JSONSerialization.jsonObject(with: data!, options: [])
self.items = jsonData as! [[String:AnyObject]]
// UI-Darstellung aktualisieren
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "markeCell", for: indexPath)
let item = items[indexPath.row]
cell.textLabel?.text = item["makename"] as? String
return cell
}
}
class FahrzeugAngabenView: UIViewController {
#IBOutlet weak var itemMarkeLabel: UILabel!
}
You could temporarily save the selected item in a variable. Something like this:
var selectedItem: Item?
func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedItem = items[indexPath.row]
self.performSegue(withIdentifier: "auto", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "auto" {
let destVc = segue.destination as! FahrzeugAngabenView
destVc.selectedItemName = selectedItem.title
selectedItem = nil
}
}
Not the most elegant solution, but i would expect this to work.
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]
}
}
}
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! :)