How to assign value of type 'String?' to type 'UIImage?' - uitableview

I am trying to load the image from the response but it is showing "Cannot assign value of type 'String?' to type 'UIImage?'". I have already loaded the labels in tableView but unable to load the image. I have used alamofire for API calls. This is my code. Thanks in advance
extension ContactVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
arrData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as? ContactCell
cell?.lblEmpName.text = self.arrData[indexPath.row].name
cell?.lblEmpDesignation.text = self.arrData[indexPath.row].designation
cell?.imgEmp.image = self.arrData[indexPath.row].profilePhoto
return cell!
}

It seems that profilePhoto is a String which may contain the URL for the image. So you must first download that image and then assign it to the image view in your cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as? ContactCell
cell?.lblEmpName.text = self.arrData[indexPath.row].name
cell?.lblEmpDesignation.text = self.arrData[indexPath.row].designation
Alamofire.request(self.arrData[indexPath.row].profilePhoto).responseImage { response in
if let image = response.result.value {
cell?.imgEmp.image = image
}
}
return cell!
}

Related

How to fill tableview with two arrays using single custom cell?

I want to display two array values in tableview using single cell.
Suppose i have two array and both contains same no of elements.
FirstArray and SecondArray. there is two label in tableview cell Lbl1 and Lbl2, now Lbl1 should fill with FirstArray and Lbl2 Should fill with SecondArray. I know that we can not use two array for uitableview datasource . I can not figure out how to do this.
Please help me.
I also tried using multiple custom tableview cells with section. but it did not give the desired result.
I have two Array -
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0
{
return datalist1.count
}
else {
return datalist2.count
}
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell?.initData(name: datalist1[indexPath.row])
return cell!
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell?.initData(lbl: datalist2[indexPath.row])
return cell!
}
}
Actual Output :
FirstCell1
FirstCell2
FirstCell3
FirstCell4
SecondCell1
SecondCell2
SecondCell3
SecondCell4
Expected Output:
FirstCell1
SecondCell1
FirstCell2
SecondCell2
FirstCell3
SecondCell3
FirstCell4
SecondCell4
Hello You not need to add two section just do as bellow.
This is your arrays.
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
Number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datalist1.coun
}
Cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell.Lbl1.text = datalist1[indexPath.row]
cell.Lbl2.text = datalist2[indexPath.row]
return cell!
}
Based on the explanation and code, you have provided, the requirement is not clear.
However, there may be two cases based on the above details:
Case-1:
Cell-1 : FirstCell1
Cell-2 : SecondCell1
Cell-3 : FirstCell2
Cell-4 : SecondCell2
Then you can implement something like below:
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (datalist1.count + datalist2.count)
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell?.initData(name: datalist1[indexPath.row])
return cell!
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell?.initData(lbl: datalist2[indexPath.row])
return cell!
}
}
Case-2:
Cell-1 : FirstCell1 SecondCell1
Cell-2 : FirstCell2 SecondCell2
Cell-3 : FirstCell3 SecondCell3
Cell-4 : FirstCell4 SecondCell4
Then you can implement something like below:
In tableview numberOfRowsInSection :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datalist1.count
}
In cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
//Single custom cell can implement both the labels
cell?.initData(name: datalist1[indexPath.row],lbl: datalist2[indexPath.row])
return cell!
}
}
You've to Declare as
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section %2 == 0 {
let cell1 = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as? FirstCell
cell1.lbl1.text = datalist1[indexPath.row]
return cell1!
}
else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? SecondCell
cell2.lbl2.text = datalist2[indexPath.row]
return cell2!
}
}
The best way is using models. You have to declare data model such as
struct MyModel {
var firstValue: String?
var secondValue: String?
}
Then you have to convert your two arrays to a single array of MyModel objects
var myData = Array<MyModel>()
Then by using for loop you can iterate over one array and fill the myData array.
for (index, _) in datalist1 {
let object = MyModel()
object.firstValue = datalist1[index]
object.firstValue = datalist2[index]
myData.append(object)
}
Then just implement tableview protocol methods and fill your custom cell with MyModel objects.
1. Create a single array from datalist1 and datalist2 using zip(_:_:), that we'll be using as dataSource for tableView.
lazy var dataList = Array(zip(self.datalist1, self.datalist2))
dataList is of type [(String, String)].
Example:
If datalist1 and datalist2 are,
let datalist1 = ["firstCell1" , "firstCell2" , "firstCell3" , "firstCell4"]
let datalist2 = ["secondCell1" ,"secondCell2" , "secondCell3" ,"secondCell4"]
then, dataList contains
[("firstCell1", "secondCell1"), ("firstCell2", "secondCell2"), ("firstCell3", "secondCell3"), ("firstCell4", "secondCell4")]
2. You need a single cell to display all that data. There is no need to create 2 different UITableViewCells for this. Example:
class CustomCell: UITableViewCell {
#IBOutlet weak var lbl1: UILabel!
#IBOutlet weak var lbl2: UILabel!
}
3. Now, your UITableViewDataSource methods look like,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
cell.lbl1.text = self.dataList[indexPath.row].0
cell.lbl2.text = self.dataList[indexPath.row].1
return cell
}

Definitive -- Get TableViewCell Images to Stop Flickering

I have struggled with this problem for AEONS, and I thought I finally came across the solution. But no. I am trying to make a tableview where the images are loaded into the correct cells and persisted, without flickering.
Here is my solution:
extension SearchViewController : BookDataProcessorDelegate {
func didFetchSmall(image: UIImage?, for indexPath: IndexPath, in tableView: UITableView) {
if tableView == resultsTableView {
if let cell = tableView.cellForRow(at: indexPath) as? BookTableViewCell {
cell.imageView?.image = image
}
}
}
func bookSearchDidFinish(with books: [Book]?) {
self.books = books == nil ? [] : Array(books!.prefix(MAX_BOOK_COUNT))
resultsTableView.reloadData()
}
}
extension SearchViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.books.count
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
BookDataProcessor.shared.fetchSmallImage(for: books[indexPath.row], at:indexPath, in:resultsTableView)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bookCell", for: indexPath) as! BookTableViewCell
cell.authorLabel.text = books[indexPath.row].authorName?[0] ?? ""
cell.titleLabel.text = books[indexPath.row].title ?? ""
cell.delegate = self
cell.indexPath = indexPath
return cell
}
It is pretty self explanatory. Basically, the BookDataProcessor.shared.fetchSmallImage triggers the didFetchSmall method, which should set the correct tableView cell. However, the cells flicker and disappear half the time. I am extremely interested to know what I am doing wrong.

CustomCell not displaying two message swift

I want to display user message and the chatbot message which is stored as array. after testing the below code the chatbot message appears but not the user message. can anyone fix this problem.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customMessageCell", for: indexPath) as! TableViewCell
cell.messages.text = messageArray[indexPath.row].userMessage
cell.messages.text = messageArray[indexPath.row].sundayMessage
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(messageArray.count)
return messageArray.count
}
You require some changes in your model in order to check the current message you have is of user type or bot type. Once you are able to figure it out then you can just use the following logic:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customMessageCell", for: indexPath) as! TableViewCell
//this is the check you need to have
if messageArray[indexPath.row].userType == "bot"{
cell.messages.text = messageArray[indexPath.row].sundayMessage
}else if messageArray[indexPath.row].userType == "user"{
cell.messages.text = messageArray[indexPath.row].userMessage
}
return cell
}
The latter line here replaces the whole string set by the former:
cell.messages.text = messageArray[indexPath.row].userMessage
cell.messages.text = messageArray[indexPath.row].sundayMessage
If you want both messages to be in the cell.messages text, you could do this:
let message = messageArray[indexPath.row]
cell.messages.text = "user: " + message.userMessage + "\nsunday: " + message.sundayMessage

Not Recognizing Clicked Cell UITableView inside UITableViewCell using NIB

I'm writing an app that have a dashboard with multiple cells. One of the cells have a question, but the answer are dynamically filled, so I decided to use a UITableView to handle it.
I set the the UITableViewCell as the delegate and dataSource of the internal UITableView and made the configurations for define the cell and the selected state.
extension SurveyTableViewCell: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
answers = model.getSurveyAnswers()
return (answers?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.idAnswer.text = alphabetQuestion[indexPath.row]
cell.answer.text = answers?[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.selectRow()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
But the click inside the cell in the internal UITableViewCell is not recognized. I need to recognize this click to after send the user answer to the server.
I saw some solutions, but using storyboard. I use only nib's on my projects.
But I still tried with an approach that I saw on youtube wich uses storyboard.
On the cell that will use the internal UITableView I declared a function to set the delegate and dataSource of the internal tableView and gave to it a tag.
extension SurveyTableViewCell {
func setTableViewDataSourceDelegate<D:UITableViewDelegate & UITableViewDataSource>(_ dataSourceDelegate: D, forRow row: Int) {
subTableView.dataSource = dataSourceDelegate
subTableView.delegate = dataSourceDelegate
subTableView.reloadData()
}
}
Than on the viewController that manage the outer UITableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.idAnswer.text = "A."
cell.answer.text = "QUALQUER COISA"
return cell
}
if retrivedCell is SurveyTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as! SurveyTableViewCell
cell.delegate = self
cell.setTableViewDataSourceDelegate(self, forRow: indexPath.row)
cell.setPositionRow(row: indexPath.row - 1)
cell.subTableView.rowHeight = UITableViewAutomaticDimension
cell.subTableView.estimatedRowHeight = 50
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return 3
}
var numberOfCells: Int = 0
if cellsToPresent != nil {
numberOfCells = cellsToPresent!.count
}
return numberOfCells + 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.selectRow()
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
The selectRow and deselectRow are methods to change the label of the cell of the inner tableView.
But still without success.
if I use the method:
tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
The app break complaining that I'm trying to dequeue different cells with the same indexPath.
Thanks for your help.
Don't use
let cell = tableView.dequeueReusableCell(withIdentifier:) in didSelect or didDeSelect methods.
Use
let cell = tableView.cellForRow(at: indexPath)
I hope this will help you.

Swift out of range.... with tableView[indexPath]

I'm getting trouble with out of range error...
The error occurs in cell.creditLabel.text = numOfDays[(indexPath as NSIndexPath).row] + "days".
And if I put tableView.reloadData() after cell.nameLabel.text = nameArray[(indexPath as NSIndexPath).row]
then nothing shows up in the simulator...
How can I fix this problem..?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MoneyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MoneyTableViewCell", for: indexPath) as! MoneyTableViewCell
cell.nameLabel.text = nameArray[(indexPath as NSIndexPath).row]
cell.creditLabel.text = numOfDays[(indexPath as NSIndexPath).row] + "days"
cell.levelLabel.text = creditArray[(indexPath as NSIndexPath).row]
cell.layoutIfNeeded()
return cell
}
You need to check your arrays. From what I can see here, numOfDays have less elements then nameArray. Also, while you are there check creditArray as well :)
Also, cast to NSIndexPath is not necessary.
Number of elements in "numOfDays" array are less than number of elements in "nameArray" array
Check that the arrays have the correct amount of items:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MoneyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MoneyTableViewCell", for: indexPath) as! MoneyTableViewCell
cell.nameLabel.text = nameArray[indexPath.row]
if numOfDays.count < indexPath.row {
cell.creditLabel.text = numOfDays[indexPath.row] + "days"
}
if creditArray.count < indexPath.row {
cell.levelLabel.text = creditArray[indexPath.row]
}
return cell
}
Also, I have removed the unnecessary casting of indexPath and the layoutIfNeeded (the cell will always draw again, since it is new).

Resources