I'm having a hard time trying to pass JSON to another view controller using a segue. So for I have only been able to use prepare(for segue, sender), but I can't get my data to populate my outlets on my view controller. Below is my first view controller. Within the prepare(for segue, sender) method you can see my commented out code that's not working. Any advice?
class ViewController2 : UIViewController, UITableViewDelegate, UITableViewDataSource {
var pictures : [Hit] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
NetworkMananger.shared.getInfo { [weak self] (results) in
guard let self = self else {return }
switch results {
case .failure(let error):
print(error)
case .success(let pictures):
self.pictures = pictures
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewController1"{
if let indexPath = self.tableView.indexPathForSelectedRow{
let vc = segue.destination as! ViewController1
// vc.downloadLabel = String(pictures[indexPath.row].downloads)
// vc.tagsLabel = pictures[indexPath.row].tags
// vc.imageData = UIImage(named: pictures[indexPath.row].previewURL)
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "ViewController1") as? ViewController1
navigationController?.pushViewController(vc!, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell123", for: indexPath) as! firstTableViewCell
cell.label?.text = String(pictures[indexPath.row].downloads)
cell.downloadPictureFromURL(from: pictures[indexPath.row].previewURL)
return cell
}
}
Below is my second view controller I want to pass my JSON to :
class ViewController1: UIViewController {
var picture : Hit!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var imageData:
UIImageView!
#IBOutlet weak var tagsLabel: UILabel!
#IBOutlet weak var downloadLabel: UILabel!
var cap : String = ""
override func viewDidLoad() {
super.viewDidLoad()
}
}
JSON
struct Response : Codable {
let hits : [Hit]
}
struct Hit : Codable {
let tags : String
let previewURL : String
let downloads : Int
}
There are a couple of things getting in your way:
Right now, in your commented code, you have a type mismatch with each property you're trying to set. For example, you're trying to set a String to downloadLabel, which is a UILabel.
At the time of prepareForSegue, the IBOutlets may not be loaded.
To get around these issues, you could set properties on ViewController1 and then initialize the views in viewDidLoad:
struct ViewController1Input {
var downloadLabelText : String
var tagsLabelText: String
var imageName: String
}
class ViewController1: UIViewController {
var picture : Hit! //careful here -- I'm not sure where this is coming from and you aren't setting it in your segue
var input : ViewController1Input?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var imageData: UIImageView!
#IBOutlet weak var tagsLabel: UILabel!
#IBOutlet weak var downloadLabel: UILabel!
var cap : String = ""
override func viewDidLoad() {
super.viewDidLoad()
if let input = input {
downloadLabel.text = input.downloadLabelText
tagsLabel.text = input.tagsLabelText
imageData.image = UIImage(named: input.imageName)
}
}
}
And in your segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewController1"{
if let indexPath = self.tableView.indexPathForSelectedRow{
let vc = segue.destination as! ViewController1
vc.input = ViewController1Input(downloadLabelText: String(pictures[indexPath.row].downloads),
tagsLabelText: pictures[indexPath.row].tags,
imageName: pictures[indexPath.row].previewURL)
}
}
}
Related
When I click on it, I want to show the picture title and year part in the cell section in the tableview, how can I do this? When we click on the row go detail page and big picture, title and year I want to show the big title and year. How can I populate the DetailViewController page?
ViewController
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
#IBOutlet var field: UITextField!
var movies = [Movie]()
override func viewDidLoad() {
super.viewDidLoad()
table.register(MovieTableViewCell.nib(), forCellReuseIdentifier: MovieTableViewCell.identifier)
table.delegate = self
table.dataSource = self
field.delegate = self
}
// Field
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchMovies()
return true
}
func searchMovies() {
field.resignFirstResponder()
guard let text = field.text, !text.isEmpty else {
return
}
let query = text.replacingOccurrences(of: " ", with: "%20")
movies.removeAll()
URLSession.shared.dataTask(with: URL(string: "https://www.omdbapi.com/?apikey=3aea79ac&s=\(query)&type=movie")!,
completionHandler: { data, response, error in
guard let data = data, error == nil else {
return
}
// Convert
var result: MovieResult?
do {
result = try JSONDecoder().decode(MovieResult.self, from: data)
}
catch {
print("error")
}
guard let finalResult = result else {
return
}
// Update our movies array
let newMovies = finalResult.Search
self.movies.append(contentsOf: newMovies)
// Refresh our table
DispatchQueue.main.async {
self.table.reloadData()
}
}).resume()
}
// Table
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieTableViewCell.identifier, for: indexPath) as! MovieTableViewCell
cell.configure(with: movies[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Show movie details
if let vc = storyboard?.instantiateViewController(identifier: "detailViewController") as? detailViewController{
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
struct MovieResult: Codable {
let Search: [Movie]
}
struct Movie: Codable {
let Title: String
let Year: String
let imdbID: String
let _Type: String
let Poster: String
private enum CodingKeys: String, CodingKey {
case Title, Year, imdbID, _Type = "Type", Poster
}
}
MovieTableViewCell
class MovieTableViewCell: UITableViewCell {
#IBOutlet var movieTitleLabel: UILabel!
#IBOutlet var movieYearLabel: UILabel!
#IBOutlet var moviePosterImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
static let identifier = "MovieTableViewCell"
static func nib() -> UINib {
return UINib(nibName: "MovieTableViewCell",
bundle: nil)
}
func configure(with model: Movie) {
self.movieTitleLabel.text = model.Title
self.movieYearLabel.text = model.Year
let url = model.Poster
if let data = try? Data(contentsOf: URL(string: url)!) {
self.moviePosterImageView.image = UIImage(data: data)
}
}
}
DetailViewController
import UIKit
class detailViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var yearLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
You can pass the data there when you create your UIViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Show movie details
if let vc = storyboard?.instantiateViewController(identifier: "detailViewController") as? detailViewController{
vc.item = movies[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
But you need to pass data first, and when you controller initialized (loaded) you can assign value to loaded views
class detailViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var yearLbl: UILabel!
var item: Movie? = nil
override func viewDidLoad() {
super.viewDidLoad()
if (let movie = item) {
titleLbl.text = item.title
yearLbl.tex = item.Year
// also image
}
}
}
I did a parse json in my listingshopvc and it shows.Now I want to select a row and it pops up another view controller but there's error just a blank page with no error. I just want to press a row and convert it to show up on another view controller.
I have listing in the following view controller: ListingShopVc,ProductDetail,TableCellData
ListingShopVC
import UIKit
import CoreData
class ListingShopVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var creditsdisplay: UILabel!
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var myUser:[User] = []
var mySecond:[Product] = []
var id:String = ""
var name:String = ""
var price:Double = 0.0
var image:String = ""
var details:String = ""
override func viewDidLoad() {
super.viewDidLoad()
fetch()
tableView.delegate = self
tableView.dataSource = self
extracted()
creditsdisplay.text = "You have 200 credits"
}
// 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 mySecond.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "hello", for: indexPath) as! TableCellData
// Configure the cell...
cell.shopTitle.text = mySecond[indexPath.row].name
cell.shopPrice.text = "$" + String(mySecond[indexPath.row].price) + "0"
cell.shopDesc.text = mySecond[indexPath.row].description
if let url = URL(string: mySecond[indexPath.row].image){
DispatchQueue.global().async {
let data = try? Data(contentsOf: url)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.shopImageView.image = image
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = mySecond[indexPath.row].id
name = mySecond[indexPath.row].name
price = mySecond[indexPath.row].price
details = mySecond[indexPath.row].description
image = mySecond[indexPath.row].image
performSegue(withIdentifier: "toDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetails"{
let vc = segue.destination as! ProductDetail
vc.productPicture = image
vc.productName = name
vc.productPrice = price
vc.productDetails = details
vc.productID = id
print(vc.productName)
}
}
func extracted(){
guard let url = URL(string: "http://rajeshrmohan.com/sport.json")
else {return}
let task = URLSession.shared.dataTask(with: url){
(data,response,error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let decoder = JSONDecoder()
let model:[Product] = try decoder.decode([Product].self, from: dataResponse)
//print(model)
for i in 0..<model.count{
self.mySecond.append(model[i])
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
func fetch(){
userList = try! context.fetch(User.fetchRequest())
tableView.reloadData()
}
}
TableViewCell
import UIKit
class TableCellData: UITableViewCell {
#IBOutlet weak var shopImageView: UIImageView!
#IBOutlet weak var shopTitle: UILabel!
#IBOutlet weak var shopPrice: UILabel!
#IBOutlet weak var shopDesc: 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
}
}
ProductDetail
import UIKit
import CoreData
class ProductDetail: UIViewController {
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var myArray:[Cart] = []
var mySecondArray:[Products] = []
var newTots:Double = 0.0
#IBOutlet weak var AddImage: UIImageView!
#IBOutlet weak var AddTitle: UILabel!
#IBOutlet weak var AddPrice: UILabel!
#IBOutlet weak var AddDesc: UILabel!
var productID = String()
var productName = String()
var productPrice:Double = 0.0
var productPicture = String()
var productDetails:String = ""
override func viewDidLoad() {
super.viewDidLoad()
AddTitle.text = productName
AddDesc.text = productDetails
AddPrice.text = String(productPrice)
if let imageUrl = URL(string: productPicture),
let imageData = try? Data(contentsOf: imageUrl) {
AddImage.image = UIImage(data: imageData)
}
}
#IBAction func add2Cart(_ sender: Any) {
let neww = Cart(context: context)
neww.image = productPicture
neww.name = productName
neww.desc = productDetails
neww.price = productPrice
neww.total = productPrice
try! context.save()
performSegue(withIdentifier: "gotoCheckout", sender: nil)
}
func fetch(){
self.mySecondArray = try! context.fetch(Products.fetchRequest())
}
}
What it shows it this [1]: https://i.stack.imgur.com/XWUt0.png
-> You just need to call a delegate method of table view didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let tag = projectEquipmentList?[indexPath.row].tag // Object data to take in next VC
let QR = R.storyboard.main.qrScannerVC()! // View Controller
QR.manageVC(projectEquipmentTag: tag!) // setter method of View controller to set data
navigationController?.pushViewController(QR, animated: true) // push to next view controller
}
I'm having trouble with my didSelectRowAt function of my tableview. For some reason, although my segue identifier and destination view are correct, when I click the row, it does not load anything. Furthermore, Xcode informs me:
Cannot assign value of type 'Double?' to type 'String?'
but I cannot find an online resource that tells me how to resolve this. For reference, the data I would like to display is from a nested JSON feed.
JSON Struct
struct PlayerStatsParent:Decodable{
let rankings: [PlayerStats]
}
struct PlayerStats:Decodable {
let personaname: String?
let score: Double?
let solo_competitive_rank: Int?
let avatar: String?
}
Cell Select Function
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? RankDetail {
destination.playerRank = rank[(rankTable.indexPathForSelectedRow?.row)!]
}
}
Destination View Controller Code
import UIKit
class RankDetail: UIViewController {
#IBOutlet var rankLabel: UILabel!
#IBOutlet var scoreLabel: UILabel!
var playerRank:PlayerStats?
override func viewDidLoad() {
super.viewDidLoad()
rankLabel.text = "\(playerRank?.solo_competitive_rank)"
scoreLabel.text = playerRank?.score
}
Also check your select method.Should be this.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
Not this;
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
If you select the cell.
Looks like the error occurs in the RankDetail class.
Here's a simple fix.
class RankDetail: UIViewController {
#IBOutlet var rankLabel: UILabel!
#IBOutlet var scoreLabel: UILabel!
var playerRank:PlayerStats?
override func viewDidLoad() {
super.viewDidLoad()
rankLabel.text = "\(playerRank?.solo_competitive_rank)"
// scoreLabel.text if of type String?, so it can't store Double? directly
// scoreLabel.text = playerRank?.score
// converting to String? should fix it
scoreLabel.text = "\(playerRank?.score)"
}
EDIT:
Present ViewController using the following code. Saves you the trouble of having to worry about segues.
if let viewController = storyboard?.instantiateViewController(withIdentifier: "NewViewController") {
present(viewController, animated: true, completion: nil)
}
Unlike many JSON requests, instead of filtering a long pre-determined JSON, I intend to send a JSON request (using AlamoFire) based on what is typed into a Search Bar, thus fetching the JSON to be parsed (with SwiftyJSON) to fill the UITableView
What I'd like to do is to use the searchTerm (typed into the searchbar) to send an AlamoFire request to Geonames using the searchTerm in the query field of geonames API request. i.e.
http://api.geonames.org/searchJSON?formatted=true&q=(searchTerm)&maxRows=3&username=demo
What I essentially cannot do is send the request AFTER filling out the Search Bar, and therefore populate the table with that data.
Below is my current code (which populates the table upon the view loading with parsed JSON data with a request for "burpham"
import UIKit
import Alamofire
import SwiftyJSON
class SearchAddLocationViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
{
#IBOutlet weak var tblJSON: UITableView!
#IBOutlet weak var searchbarValue: UISearchBar!
weak open var delegate: UISearchBarDelegate?
var arrRes = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad()
{
super.viewDidLoad()
}
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("http://api.geonames.org/searchJSON?q=\(searchTerm)&maxRows=5&username=garethallenstringer").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["geonames"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tblJSON.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "addLocProtoCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["name"] as? String
cell.detailTextLabel?.text = dict["countryName"] as? String
return cell
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrRes.count
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to listen func searchBarTextDidEndEditing(_ searchBar: UISearchBar) method of UISearchBar delegate for populating your UITableView
class ViewController: UIViewController, UISearchBarDelegate {
#IBOutlet private weak var searchBar: UISearchBar?
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar?.delegate = self
}
//MARK: UISearchBarDelegate methods
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//You can do your request via Alamofire to populate tableView
}
}
UPDATE #1
import UIKit
import Alamofire
import SwiftyJSON
class SearchAddLocationViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate
{
#IBOutlet weak var tblJSON: UITableView!
#IBOutlet weak var searchbarValue: UISearchBar!
var arrRes = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad()
{
super.viewDidLoad()
self.searchbarValue.delegate = self
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("http://api.geonames.org/searchJSON?q=\(searchTerm)&maxRows=5&username=garethallenstringer").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["geonames"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tblJSON.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "addLocProtoCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["name"] as? String
cell.detailTextLabel?.text = dict["countryName"] as? String
return cell
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrRes.count
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I integrated alamofire, but I got one problem to use data within server communication.
Before I tell about my problem I will show my code:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var MainViewTable: UITableView!
#IBOutlet weak var SidesOpen: UIBarButtonItem!
#IBOutlet weak var GroupButton: UIButton!
var apps:[sample] = [sample]()
override func viewDidLoad() {
super.viewDidLoad()
SidesOpen.target = self.revealViewController()
SidesOpen.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.setUpSample()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setUpSample() {
Alamofire.request(.GET, "http://{url}").responseJSON{ (request, response, data, error) in
var json = JSON(data!)
for var index = 0; index < json["store"].count; ++index{
var marketinfo = json["store"][index]["name"].stringValue
var imageUrl = json["store"][index]["img"].stringValue
let sample_menu = sample(marketInfo: marketinfo, imageName: imageUrl, button: "")
self.apps.append(sample_menu)
}
}
print(self.apps)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return apps.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print(setUpSample())
let cell: TblCell = tableView.dequeueReusableCellWithIdentifier("marketCell") as! TblCell
let sample = apps[indexPath.row]
cell.setCell(sample.marketInfo , imageName: sample.imageName, Coupon: "s/t.jpeg")
return cell
}
}
Within setUpSample function I have got one problem that I have no idea to passing or taking out JSON data. In the function I tried to print result what I can have from it however, the result was empty.