Second Level UITableView not working in SWIFT - swift

I've got a UITableView like so:
import UIKit
import SDWebImage
class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var ConversationList: UITableView!
let apiService = APIService()
var conversations: [Conversation] = []
var selectedConversation: Int?
override func viewDidLoad() {
super.viewDidLoad()
ConversationList.separatorStyle = .none
ConversationList.dataSource = self
ConversationList.delegate = self
// load Conversations
self.apiService.getConversations(completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
print("NUMBER OF CONVERSATIONS: ", conversations.count)
self.conversations = conversations
self.ConversationList.reloadData()
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.conversations.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatViewCell", for: indexPath) as! ChatViewCellController
cell.UserName.text = self.conversations[indexPath[1]].participants![1].username
let imgURL = URL(string: self.conversations[indexPath[1]].participants![1].profileimage!)
cell.UserLogo.sd_setImage(with: imgURL, placeholderImage: UIImage(named: "icon.turq.png"))
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected row number ", indexPath)
self.selectedConversation = indexPath[1]
self.performSegue(withIdentifier: "ChatToChatDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! ChatDetailViewController
destination.name = self.conversations[self.selectedConversation!].participants![1].username!
destination.img = self.conversations[self.selectedConversation!].participants![1].profileimage!
}
}
The UITableViewCell sits in a separate, simple class like so:
import UIKit
class ChatViewCellController: UITableViewCell {
#IBOutlet weak var UserLogo: UIImageView!
#IBOutlet weak var UserName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.UserLogo.clipsToBounds = true
self.UserLogo.layer.cornerRadius = self.UserLogo.frame.size.width / 2
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
As shown above, selecting a specific cell (i.e conversation) in the UITableView loads another UIViewController via segue. That UIViewController then contains another UITableView:
import UIKit
import SDWebImage
class ChatDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var UserLogo: UIImageView!
#IBOutlet weak var UserName: UILabel!
#IBOutlet weak var MessageList: UITableView!
var name: String = ""
var img: String = ""
let apiService = APIService()
var messages: [ChatMessage] = []
override func viewDidLoad() {
super.viewDidLoad()
// prepare header section:
self.MessageList.separatorStyle = .none
let imgURL = URL(string: self.img)
self.UserLogo.sd_setImage(with: imgURL, placeholderImage: UIImage(named: "icon.turq.png"))
self.UserLogo.clipsToBounds = true
self.UserLogo.layer.cornerRadius = self.UserLogo.frame.size.width / 2
self.UserName.text = self.name
// TODO: load messages
self.MessageList.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MessageViewCell", for: indexPath) as! ChatMessageViewCellController
cell.ChatMessageText.text = "foo"
return cell
}
}
The UITableViewCell for the above UITableView is again sitting in a separate, simple class:
import UIKit
class ChatMessageViewCellController: UITableViewCell {
#IBOutlet weak var ChatMessageBubble: UIView!
#IBOutlet weak var ChatMessageText: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
print("I'm awake")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Everything works fine up until a certain point. The first Table View loads and shows all Chats. Clicking on a Chat loads the new View with the header section, showing the Logo and Name of the Chat partner at the top of the screen and a Table underneath it. However, that table does not contain anything. To my understanding, with this code, it should show 10 rows saying "foo" and print 10 times "I'm awake" to the console.
What am I missing or doing wrong here?

In your second ViewController, you are not setting UITableViewDelegate and UITableViewDataSource. What you need to add to viewDidLoad:
MessageList.delegate = self
MessageList.dataSource = self
Bonus, for future change self.selectedConversation = indexPath[1] in first view controller to prevent bug - it will always take second model, even if you have only one.
Also, try to avoid upper case for attributes, e.g. MessageList -> messageList.

Related

Problem using an extension to load image into a custom cell

My last question for today. I'm too newby with swift to know what's happening but my problem is as follows:
I've use the next extension to load an image from a URL and it works when I use it in a imageView in a normal view, but when the imageView is inside of a custom cell It doesn't recognize de method so I can`t use the extension. What am I doing wrong? Thank you all in advances.
My code for the extesion is:
import UIKit
extension UIImageView {
func loadFrom(URLAddress: String) {
guard let url = URL(string: URLAddress) else {
return
}
DispatchQueue.main.async { [weak self] in
if let imageData = try? Data(contentsOf: url) {
if let loadedImage = UIImage(data: imageData) {
self?.image = loadedImage
}
}
}
}
}
And the code for the table where I try to use it into a custom cell:
import UIKit
class EventosCustomCellController: UITableViewCell {
#IBOutlet weak var imEvento: UIView!
#IBOutlet weak var txtNombreEvento: UILabel!
#IBOutlet weak var txtFechaEvento: UILabel!
#IBOutlet weak var txtEstadoEvento: UILabel!
}
class ListaEventosTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Eventos"
}
// MARK: - Table view data source
override func viewWillAppear(_ animated: Bool) {
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventos.contarEventos()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prototipoCeldaEvento", for: indexPath) as! EventosCustomCellController
let evento = eventos.buscarEventoPorID(id: indexPath.row)
cell.txtNombreEvento?.text = evento?.nombre
cell.txtFechaEvento?.text = evento?.fecha
cell.txtEstadoEvento?.text = evento?.tipo
if evento?.tipo == "deportivo"{
cell.backgroundColor = .blue}
else if evento?.tipo == "cultural"{
cell.aplicarFondoDegradado()
cell.backgroundColor = .green}
else{
cell.backgroundColor = .red}
cell.layer.masksToBounds = true
cell.layer.cornerRadius = 10
//There is no method loadFrom when I try to use as follows
cell.imEvento?.loadFrom(URLAddress: (evento?.imagenes![0])!)
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
}
After updating code my problem is that the cell isn`t growthn appropiately
I think, you better use UIImageView instead of UIView because your extension for UIImageView and you can access to this.
#IBOutlet weak var imEvento: UIImageView!
and then it is good to override the prepareForReuse method like this:
override func prepareForReuse() {
super.prepareForReuse()
imEvento.image = nil
}
you can read more about the prepareForReuse method from apple documentation

Label.text Error! - Unexpectedly found nil while implicitly unwrapping an Optional value

I am new to iOS development and I found an error that I can not get past. I have read a lot online and on Stackoverflow but I don't understand why this error keeps coming up.
Upon testing and doing breakpoints I think I was able to get the data needed problem is when I display it on screen wit uilabel.
import UIKit
class ArtistViewController: UIViewController, artistViewModelDelegate {
#IBOutlet weak var artwork: UIImageView!
#IBOutlet weak var artistName: UILabel!
#IBOutlet weak var albumName: UILabel!
func loadArtistViewModel(data: ArtistViewModel) {
guard let artistData = data as? ArtistViewModel else {}
artistName.text = artistData.artistName //Unexpectedly found nil while unwrapping an Optional value
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Hope you guys can help me on this one, Thank you so much!
EDIT
ViewController where the instance of ArtistViewController gets called
import UIKit
import Alamofire
import SwiftyJSON
protocol artistViewModelDelegate {
func loadArtistViewModel(data: ArtistViewModel)
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, artistSearchDelegate {
#IBOutlet weak var tableView: UITableView!
var data: [ArtistItem] = []
var delegate: artistViewModelDelegate?
var artistViewModel: ArtistViewModel?
var params = [API_CONSTANTS.URL_TYPES.PARAMETERS.TERM: "Maroon 5",API_CONSTANTS.URL_TYPES.PARAMETERS.COUNTRY: API_CONSTANTS.AU, API_CONSTANTS.URL_TYPES.PARAMETERS.MEDIA: API_CONSTANTS.URL_TYPES.PARAMETERS.MUSIC]
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = ArtistViewController()
getItunesData()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "artistCell", bundle: nil), forCellReuseIdentifier: "artistCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "artistCell", for: indexPath) as! artistCell
cell.artistName.text = data[indexPath.row].artistName
cell.albumName.text = data[indexPath.row].albumName
cell.genre.text = data[indexPath.row].genre
cell.trackPrice.text = "$\(String(data[indexPath.row].trackPrice))"
cell.albumArtwork.load(url: data[indexPath.row].artwork)
cell.layer.cornerRadius = 5
cell.layer.masksToBounds = true
return cell
}
//Mark: To Artist ViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
artistViewModel = ArtistViewModel(artist: data[indexPath.row])
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
delegate?.loadArtistViewModel(data: artistViewModel!)
self.navigationController?.pushViewController(artistViewController, animated: true)
}
func getItunesData(){
Alamofire.request(API_CONSTANTS.URL_TYPES.URL, method: .get, parameters: params).responseJSON
{ response in
if response.result.isSuccess {
let json = JSON(response.result.value)
self.data = ArtistModel(json: json).artistItems
self.tableView.reloadData()
} else {
}
}
}
func didTapSearch(artist: String) {
params = [API_CONSTANTS.URL_TYPES.PARAMETERS.TERM:"\(artist)"]
getItunesData()
}
#IBAction func searchButton(_ sender: Any) {
let popupSearchVC = storyboard?.instantiateViewController(withIdentifier: "popupSearchView") as! PopupViewController
popupSearchVC.delegate = self
present(popupSearchVC, animated: true, completion: nil)
}
}
The problem is the protocol. You set delegate to an instance which is not the instance in the storyboard.
But with the given code you don't need the protocol at all.
Delete
protocol artistViewModelDelegate {
func loadArtistViewModel(data: ArtistViewModel)
}
...
var delegate: artistViewModelDelegate?
...
self.delegate = ArtistViewController()
and the protocol conformance, then replace
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
artistViewModel = ArtistViewModel(artist: data[indexPath.row])
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
delegate?.loadArtistViewModel(data: artistViewModel!)
self.navigationController?.pushViewController(artistViewController, animated: true)
}
with
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
artistViewController.artistViewModel = ArtistViewModel(artist: data[indexPath.row])
self.navigationController?.pushViewController(artistViewController, animated: true)
}
And in ArtistViewController you have to use a temporary variable for the model because the outlets are not connected (yet) right after instantiation.
Replace the code in the question with
import UIKit
class ArtistViewController: UIViewController, artistViewModelDelegate {
#IBOutlet weak var artwork: UIImageView!
#IBOutlet weak var artistName: UILabel!
#IBOutlet weak var albumName: UILabel!
var artistViewModel : ArtistViewModel!
override func viewDidLoad() {
super.viewDidLoad()
artistName.text = artistViewModel.artistName
}
}

in the Table view I want to add some images but when I run it doesn't show and I already import the images in assets.xcassets

I already make an array to access the picture.. Can someone help? thanks
Here is my code:
import UIKit
class MainMenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let labeltitle = ["Acme De la Vie Tee", "Anti Social Social Club Black Hoodie", "CDG Play Gold Black Tee"]
let labelprice = [("RM 240"), ("RM 305"), ("RM 418")]
let myImage = [UIImage(named: "adlv1"), UIImage(named: "assc"), UIImage(named: "cdg1")]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labeltitle.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MainMenuTableViewCell
cell.label11?.text = labeltitle[indexPath.row]
cell.label2?.text = labelprice[indexPath.row]
cell.myImage.image = self.myImage[indexPath.row]
return cell
}
}
Here is my code for MainMenuTableViewCell
import UIKit
class MainMenuTableViewCell: UITableViewCell {
#IBOutlet weak var label11: UILabel!
#IBOutlet weak var label3: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var myImage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
you need to register your tableviewcell in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(MainMenuTableViewCell.self, forCellReuseIdentifier: "cell")
}
Apple Documentation
More Info
Did u add constraint ?
Select to viewController and do reset the suggested constraint and im sure it will be solved
cell.myImage.image = self.myImage[indexPath.row]
try to change your image Array name

Object is not setup correctly when passing it across classes

I'm having trouble pushing new ViewController in swift.
class CartViewController: UITableViewController {
static var items:[Item] = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return CartViewController.items.count
}
//if cell is selected
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "PaymentController") as! PaymentController
print(CartViewController.items[indexPath.row].title)
vc.item? = CartViewController.items[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
The print function up there gives the right result, but there's nothing showing up when the new vc is pushed. debugger shows the
vc.item
is nil.
This is what I have in PaymentController class
class PaymentController: UIViewController {
#IBOutlet weak var myImage: UIImageView?
#IBOutlet weak var myTitle: UILabel?
#IBOutlet weak var myPrice: UILabel?
var item:Item?
override func viewDidLoad() {
super.viewDidLoad()
myImage?.image = UIImage(named: item!.image)
myTitle?.text = item!.title
myPrice?.text = String(item!.price)
}
}
Any hints or ideas will be appreciated:))
In tableView(_,didSelectRowAt:) try removing the ? in line 3.
I.e., replace vc.item? = (...) with vc.item = (...)

Passing data from tableView to ViewController in Swift

I have an App that i'm trying to adapt exactly how i want
I have been following a Youtube tutorial of Seemu Apps to make it but I need to finish it adding an optional ViewController
This app has 2 tableViews showing vehicles and if we click in one row of the first tableView then second tableView will show us a list of selected vehicles.
Here is what we have until now: (image link , because i haven't got ten points reputation on stackOverFlow)
http://subefotos.com/ver/?65ba467040cb9280e8ec49644fd156afo.jpg
All is running perfect, but i want to be able to display information in an optional detailViewController (label with a detailed description of each vehicle and a bigger image of this ) depending of which vehicle we click in the secondTableViewControlle (or modelViewController in the App) exactly how i was following in the tutorial between tableViews
i know that we need to passing data through prepareForSegue method , i have understood this making the steps in the tutorial but when we have 2 tableviewControllers
For example : if we want to display a last viewController with information of Ferrari 458 and a great picture of this car
What do we need to do exactly to show information of each vehicle?
PD : I'm beginner in the programming world, maybe i would need to see it in a very simple way
The whole code:
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var selMake = String()
#IBOutlet var tableView : UITableView!
var transportData : [String] = ["Car", "Plane", "Motorcycle", "Truck" , "Train", "Bicycle" , "Helicopter"]
//////////////////////////////////////////
//viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
//Register custom cell
var nib = UINib(nibName: "customCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
}
//Numbers of rows in Section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.transportData.count
}
//cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
///// Static Cell (no valid for custom cells)
/*
var cell : UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.transportData[indexPath.row]
return cell
*/
var cell:customCellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as customCellTableViewCell
cell.lblTrans.text = transportData[indexPath.row]
cell.imgTrans.image = UIImage (named: transportData[indexPath.row])
return cell
}
//height
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 90
}
//didSelectRowAtIndexPath
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Fila \(transportData[indexPath.row]) seleccionada")
selMake = transportData[indexPath.row]
performSegueWithIdentifier("modelView", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "modelView") {
var vc = segue.destinationViewController as modelViewViewController
vc.selMake = selMake
}
}
import UIKit
class customCellTableViewCell: UITableViewCell {
#IBOutlet weak var imgTrans: UIImageView!
#IBOutlet weak var lblTrans: 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
}
}
import UIKit
class modelViewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//////////////////////////////////
var selMake = String()
var tableData : [String] = []
#IBOutlet var tableView: UITableView!
//////////////////////////////////
override func viewDidLoad() {
super.viewDidLoad()
//Register custom cell
var nib = UINib(nibName: "customCell2", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
switch selMake {
case "Car" :
tableData = ["Ferrari 458", "La Ferrari"]
case "Plane" :
tableData = ["Iberia"]
case "Motorcycle" :
tableData = ["Kawasaki Ninja", "Yamaha Aerox"]
case "Truck" :
tableData = [ "Camion transporte"]
case "Train" :
tableData = [ "Ave" ]
case "Bicycle" :
tableData = ["BMX"]
case "Helicopter" :
tableData = ["HelicopteroCombate"]
default:
println("Sel Make \(selMake)")
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
/* var cell : UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.tableData[indexPath.row]
return cell*/
var cell:customCell2TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as customCell2TableViewCell
cell.lbl2text.text = self.tableData[indexPath.row]
cell.img2image.image = UIImage (named: tableData[indexPath.row])
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Row \(indexPath.row)selected")
performSegueWithIdentifier("detailView", sender: self)
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 90
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detailView") {
var vc = segue.destinationViewController as DetailViewController
}
}
import UIKit
class customCell2TableViewCell: UITableViewCell {
#IBOutlet var lbl2text: UILabel!
#IBOutlet var img2image: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
import UIKit
class DetailViewController: UIViewController {
#IBOutlet var imgDetail: UIImageView!
#IBOutlet var lblDetail: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
Try this.
ModelViewViewController
var selectedImage:String?
var selectedLabel:String?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Row \(indexPath.row)selected")
selectedImage = self.tableData[indexPath.row]
selectedLabel = self.tableData[indexPath.row]
performSegueWithIdentifier("detailView", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detailView") {
var vc = segue.destinationViewController as DetailViewController
vc.img = selectedImage
vc.lblDetail = selectedLabel
}
}
class DetailViewController: UIViewController {
#IBOutlet var imgDetail: UIImage!
#IBOutlet var lblDetail: UILabel!
var img:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imgDetail = UIImage(named: img)
}
This should work.