I have taken a UITableView and deque a UITableViewCell XIB and set data to a label in it, also I have UICollectionView in UITableViewCell XIB in which I have to set images in a UICollectionViewCell.
UIImageView is in UICollectionViewCell and respective UICollectionView is in UITableViewCell so how to set an image to that UIImageView?
also have tableview delegate function in one file
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.discoverData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryTableViewCell", for: indexPath) as! discoveryTableViewCell
let data = discoverData[indexPath.row]
cell.messageLbl.text = (data["content"] as! String)
// This how normally image is set in tableview, how to do with collectionview
cell.userImage.pin_updateWithProgress = true
let riderImage = URL(string: (data["profile_pic"] as! String))
cell.userImage.pin_setImage(from: riderImage, placeholderImage: nil, completion: nil)
return cell
}
collection view delegate function in UITableViewCell.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numCell
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "discoverCell", for: indexPath as IndexPath) as! discoverCollectionViewCell
cell.postImage.backgroundColor = .cyan
return cell
}
Here is a picture of how it looks yellow box is UICollectionView and cyan color box is UIImageView.
In cellForRowAt indexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryTableViewCell", for: indexPath) as! discoveryTableViewCell
if let data = discoverData[indexPath.row] as? [String: Any] {
cell.messageLbl.text = (data["content"] as! String)
cell.data = data
}
return cell
}
In your "discoveryTableViewCell" (It should be "DiscoveryTableViewCell") :
var data: [String: Any] {
didSet {
collectionView.reloadData()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "discoverCell", for: indexPath as IndexPath) as! discoverCollectionViewCell
if let profilePic = data["profile_pic"] as? String {
let riderImage = URL(string: profilePic)
cell.postImage.pin_setImage(from: riderImage, placeholderImage: nil, completion: nil)
}
return cell
}
You should follow the proper style guide and naming conventions in Swift.
Please check this URL for better understanding:
https://github.com/raywenderlich/swift-style-guide
Related
I am working on a location-based reminder app. I show all reminders that user created on a table view. I have also UISwitch on every cell. I want that UISwitch disables/enables reminders individually, not all notifications. I couldn't figure it out.
extension MainViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
//switch
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = indexPath.row
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
cell.accessoryView = swicthView
let itemToRemove = self.items![indexPath.row]
let notifToRemove: String = itemToRemove.notifID!
return cell
}
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
}
I believe your code is not working because cells are reused and actions on specific cells should be handled from the cell class rather than from ViewController. Move this code into the UITableViewCell code.
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = switchViewTag!
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
and add a new property in the UITableViewCell
weak var switchViewTag: Int?
Modify your cellForRowAt delegate method to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
cell.switchViewTag = indexPath.row
return cell
}
I need to reloadData collectionView. Because I am append to array in function. I need to do reload data for this, but I get the error "Type of expression is ambiguous without more context". Data does not appear to be added because I did not reloadData. how can i use collectionView.reloadData? How can I reload data on TableView and CollectionView?
class denemeView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var davetiyeKategori = [String]()
var davetiyeKatIsım = [String]()
var storedOffsets = [Int: CGFloat]()
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return kategoriIsımYeni[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return kategoriIsımYeni.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? CategoryRow else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? CategoryRow else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
func collectionReloadData(){
DispatchQueue.main.async(execute: {
self.collectionView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
davetiyeKategoriBul()
}
#objc func davetiyeKategoriBul(){
if let baslik = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for review in baslik {
if let soru_baslik = review["ISIM"] as? String {
let s = String(describing: soru_baslik)
self.kategoriIsımYeni.append(s)
DispatchQueue.main.async {
// self.tableViewKategoriler.reloadData()
}} }}
extension denemeView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return model[collectionView.tag].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! anaSayfaCell
let rowValue = model[collectionView.tag][indexPath.item]
print("urlNew", rowValue)
let urlNew = URL(string: (rowValue))
cell.denemeImage.sd_setImage(with: urlNew)
// cell.backgroundColor = model[collectionView.tag][indexPath.item]
print("model[collectionView.tag][indexPath.item]", model[collectionView.tag][indexPath.item])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Collection view at row \(collectionView.tag) selected index path \(indexPath)")
}
}
I have a tableView and collectionView in one view controller.
in tableView I have title description and in collectionView I have lable .
I want on collectionView label selection tableView content should change .
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Bookmark.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewBookmark.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BookMarkCollectionViewCell
cell.lblTitle.text = Bookmark[indexPath.row]
cell.backgroundColor = UIColor.white
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionViewBookmark.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.blue
self.selectedIndexPath = indexPath
//
let newsDict = arrNewsData[indexPath.row]
if (indexPath.row == 1)
{
let cell1 = tableViewBookMark.cellForRow(at: indexPath) as! BookMarkFirstTableViewCell
cell1.lblTitle.text = newsDict["title"] as! String
tableViewBookMark.reloadData()
}
tableViewBookMark.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewBookMark.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BookMarkFirstTableViewCell
let dict = arrNewsData[indexPath.row]
cell.lblTitle.text = dict["title"] as! String
// cell.imgBookMark.image = dict["image_url"]
let url = URL(string: dict["image_url"] as! String)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if data != nil{
DispatchQueue.main.async {
let image = UIImage(data: data!)
cell.imgBookMark.image = image
}
}
}.resume()
return cell
}
See my inline comments.
var tempCell: BookMarkFirstTableViewCell?
//Inside cellForRowAt indexPath
tempCell = cell
//Inside (collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
tempCell.lblTitle.text = newsDict["title"] as! String
You're reloading the tableView after updating the values in the cell,
tableViewBookMark.reloadData()
This will trigger the data source function including cellForRowAt, so you will lose your updated values a solution to this is to have a global variable in the UIViewController and check its values inside the cellForRowAt and update it in the collectionView DidSelect .
Extra tip: you don't need to reload all the tableView for a single change you can use
tableView.reloadRows(at: [indexPath], with: .top)
to reload only number of selected cells in the tableView
I'm trying to put a UICollectionView in a UITableViewCell, but I guess I'm doing wrong...
So here is my code :
PanierMenuCell is UITableViewCell
extension PanierMenuCell : UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return listProduit.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellIdentifier = "PanierCollectionCell"
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as? PanierCollectionCell else {
fatalError("erreur de conversion !!")
}
cell.ONom.text = listProduit[indexPath.row].nomProduct
cell.OQtt.text = "x\(listProduit[indexPath.row].nbProduct)"
cell.OImage.image = UIImage(named: "test")
cell.backgroundColor = .gray
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.cornerRadius = 5
return cell
}
}
Here is my class calling the UITableViewCell :
extension PanierVC : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listPanierProduct.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "PanierMenuCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PanierMenuCell else {
fatalError("erreur de conversion !!")
}
let produit = listPanierProduct[indexPath.row].product as! Menu
let listProduit = produit.getListProducts()
cell.listProduit = listProduit
cell.OTotal.text = "Total: \(produit.prix)0€"
cell.ONomMenu.text = "Menu : \(produit.nom)"
cell.backgroundColor = .gray
return cell
}
So here is the problem :
When I'm using several cell with collectionView inside without scrolling the tableview, that's working well, the problem is when I need to scroll down the tableView (the tableview is so reloading the cell), that's causing an error : "Thread 1: Fatal error: Index out of range" in
cell.ONom.text = listProduit[indexPath.row].nomProduct
The item insides the UICollectionView are king of mixed together, I mean that a tableviewcell will take some items which are suppose to be in other tableviewcell, like if the cell.listproduit was mixed... Don't know how clear I was, but I can explain more if needed.
Any ideas on how I should solve this ?
Thank's a lot
You need to reload your UICollectionView after changing the data source. This will update the number of items in the collection view.
So after this line:
cell.listProduit = listProduit
Add this line (Assuming you have a property referencing your UICollectionView):
cell.collectionView.reloadData()
below code supposed to call usernames from an array and user able to tap on the desired username. call to that array is a success and I can see the usernames but won't able to tap into it. and it don't even print "didSelectRowAt". appreciate your help. thanks.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return autoComplete.count
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let selectedCell: MentionUserCell = tableView.cellForRow(at: indexPath)! as! MentionUserCell
let selectedWord: String = selectedCell.username!.text! + " "
print("didSelectRowAt")
var string: NSString = NSString(string: self.commentTextView.text!)
string = string.replacingCharacters(in: otoCadang.sharedInstance.foundRange, with: selectedWord) as NSString
// change text field value
commentTextView.text = string as? String
// change cursor position
let positionOriginal = commentTextView.beginningOfDocument
if let cursorLocation: UITextPosition = self.commentTextView.position(from: positionOriginal, offset: otoCadang.sharedInstance.foundRange.location + selectedWord.characters.count)
{
self.commentTextView.selectedTextRange = self.commentTextView.textRange(from: cursorLocation, to: cursorLocation)
}
// remove suggestion
self.autoComplete.removeAll()
self.tableView.reloadData()
tableView.isHidden = true
}
Check List
tableview delegate is set properly
tableview selection mode == No Selection, then change to single selection
function name is correct func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
are you using any tap gesture on this view? remove it.
If you are editing UITextView will can't call to `didselectRowAt. Try to get the Your MentionUserCell, IndexPath in UITextView's methods and handle there. Example:
func textViewDidChange(_ textView: UITextView) {
var parentView = textView as UIView
while !(parentView is UITableViewCell) {
parentView = parentView.superview!
}
if let cell: MentionUserCell = parentView as? MentionUserCell {
if let indexPath = self.tableView.indexPath(for: cell) {
let yourIndexPath = indexPath
}
}
//your code
}
Hope it will help you.
When are using two or more cells the didSelectRow method is not working Well. You can use a button in cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifierName", for: indexPath) as! MentionUserCell
cell.username!.text = autoComplete[indexPath.row]
let button = cell.viewWithTag(101) as! UIButton
button.cellclick.addTarget(self, action: #selector(functionName(sender:)), for: .touchDown)
return cell
}
Now you get the indexPath:-
func functionName(sender:UIButton) {
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableview)
let indexPath = self.tableview.indexPathForRow(at: buttonPosition)
print(indexPath.row)
}