people, I have this issue when I try back image from different cell
(Thread 1: Fatal error: Index out of range)
what I'm doing here ?
I'm trying to build an Instagram clone and in my home view controller that what should posts show up. I make navigation with a table view and that table view has 2 cell with the different identifier. cell number 1 it's a header that brings data from users table to my username label and profile image. and cell number 2 its for posts its should bring post data like image and caption. I use firebase database.
my code :
import UIKit
import FirebaseAuth
import FirebaseDatabase
class HomeViewController: UIViewController ,UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
var posts = [Post]()
var users = [UserD]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
loadposts()
userDetal()
// var post = Post(captiontxt: "test", photoUrlString: "urll")
// print(post.caption)
// print(post.photoUrl)
}
func loadposts() {
Database.database().reference().child("posts").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let captiontxt = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let post = Post(captiontxt: captiontxt, photoUrlString: photoUrlString)
self.posts.append(post)
print(self.posts)
self.tableview.reloadData()
}
}
}
func userDetal() {
Database.database().reference().child("users").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let usernametxt = dict["username"] as! String
let profileImageUrlString = dict["profileImageUrl"] as! String
let user = UserD(usernametxt: usernametxt, profileImageUrlString: profileImageUrlString)
self.users.append(user)
print(self.users)
self.tableview.reloadData()
}
}
}
#IBAction func logout(_ sender: Any) {
do {
try Auth.auth().signOut()
}catch let logoutErrorr{
print(logoutErrorr)
}
let storyboard = UIStoryboard(name: "Start", bundle: nil)
let signinVC = storyboard.instantiateViewController(withIdentifier: "SigninViewController")
self.present(signinVC, animated: true, completion: nil)
}
}
extension HomeViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = tableview.dequeueReusableCell(withIdentifier: "imagecell", for: indexPath) as! PostCellTableViewCell
cell.postimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.captionLabel.text = posts[indexPath.row].caption
let photoUrl = posts[indexPath.row].photoUrl
getImage(url: photoUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.postimage.image = photo
}
}
}
}
return cell
} else if indexPath.row == 1 {
let cell = tableview.dequeueReusableCell(withIdentifier: "postcell", for: indexPath) as! HeaderTableViewCell
cell.userimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.usernamelabel.text = users[indexPath.row].username
//Error showing here????????????????????????????????????
let profileImageUrl = users[indexPath.row].profileImageUrl
getImage(url: profileImageUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.userimage.image = photo
}
}
}
}
return cell
}
return UITableViewCell()
}
func getImage(url: String, completion: #escaping (UIImage?) -> ()) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
if error == nil {
completion(UIImage(data: data!))
} else {
completion(nil)
}
}.resume()
}
}
try this one.
cell.tag = indexpath.row
What is the content of users array ?
Are you sure you want to define as many sections as users or as many rows ?
In this case use
func numberOfRows(in tableView: NSTableView) -> Int {
return users.count
}
As explained, you need to rewrite completely cellForRowAt
It should look like this :
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if row < users.count {
let user = users[row]
if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), owner: self) {
(cellView as! NSTableCellView).textField?.stringValue = user.name
// do the same for all the fields you need to set
return cellView
} else {
return nil
}
}
return nil
}
thanx, my friend, I found a good way to contain my cell. for post cell, i just use cellForRowAt and but the post data. for header cell i use viewForHeaderInSection
and but my user data with heightForHeaderInSection. to make the high for a view
Every time I tap on the like button (heart icon) the cell gets updated just fine but it gets duplicate too. I researched and tried to figure it out on my own but I couldn't see why it's being duplicated.
I investigated and I found that probably this might be a problem with the cellForRowAtIndexPath function but that function looks good to me.
Here's the relevant code:
HomeController.swift:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout, HomePostCellDelegate {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(HomePostCell.self, forCellWithReuseIdentifier: cellId)
let refreshControll = UIRefreshControl()
refreshControll.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
if #available(iOS 10.0, *) {
collectionView?.refreshControl = refreshControll
} else {
// Fallback on earlier versions
}
setupNavigationItems()
fetchAllPost()
}
#objc func handleUpdateFeed() {
handleRefresh()
}
#objc func handleRefresh() {
posts.removeAll()
fetchAllPost()
}
fileprivate func fetchAllPost() {
fetchPosts()
fetchFollowingUserIds()
}
fileprivate func fetchFollowingUserIds() {
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("following").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userIdsDictionary = snapshot.value as? [String: Any] else { return }
userIdsDictionary.forEach({ (key, value) in
FIRDatabase.fetchUserWithUid(uid: key, completion: { (user) in
self.fetchPostsWithUser(user: user)
})
})
}) { (err) in
print("failed to fetch following users ids:", err)
}
}
var posts = [Post]()
fileprivate func fetchPosts() {
guard let currentUserID = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.fetchUserWithUid(uid: currentUserID) { (user) in
self.fetchPostsWithUser(user: user)
}
}
fileprivate func fetchPostsWithUser(user: User) {
let ref = FIRDatabase.database().reference().child("posts/\(user.uid)/")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if #available(iOS 10.0, *) {
self.collectionView?.refreshControl?.endRefreshing()
} else {
// Fallback on earlier versions
}
guard let dictionaries = snapshot.value as? [String: Any] else { return }
dictionaries.forEach({ (key,value) in
guard let dictionary = value as? [String: Any] else { return }
var post = Post(user: user, dictionary: dictionary)
post.id = key
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
FIRDatabase.database().reference().child("likes").child(key).child(uid).observe(.value, with: { (snapshot) in
if let value = snapshot.value as? Int, value == 1 {
post.hasLiked = true
} else {
post.hasLiked = false
}
self.posts.append(post)
self.posts.sort(by: { (p1, p2) -> Bool in
return p1.creationDate.compare(p2.creationDate) == .orderedDescending
})
self.collectionView?.reloadData()
print("fetch post with user reload data")
}, withCancel: { (err) in
print("Failed to fetch info for post")
})
})
}) { (error) in
print("Failed to fetch posts", error)
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height: CGFloat = 310 // username + userProfileImageView
return CGSize(width: view.frame.width - 27.5, height: height)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HomePostCell
cell.post = posts[indexPath.item]
cell.delegate = self
cell.photoImageView.image = nil
// Makes cell corners round
cell.layer.masksToBounds = true
cell.layer.cornerRadius = 17
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func didLike(for cell: HomePostCell) {
guard let indexPath = collectionView?.indexPath(for: cell) else { return }
var post = self.posts[indexPath.item]
guard let postId = post.id else { return }
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
let values = [uid : post.hasLiked == true ? 0 : 1]
FIRDatabase.database().reference().child("likes").child(postId).updateChildValues(values) { (err, _) in
if let err = err {
print("Failed to like post", err)
return
}
print("Successfully liked post!")
post.hasLiked = !post.hasLiked
self.posts[indexPath.item] = post
self.collectionView?.reloadItems(at: [indexPath])
}
}
}
Let me know if you have any questions, Thank you!
I have created a Form , in which I am using Textfields and TextField Dropdown. While I am scrolling the TableView textfields values got lost and textfield dropdown values set to default automatically.
Here is my code :
enum TextFieldTags:Int {
case ProspectName = 10, VisitDate, VisitType, Industry, Source, DissSummary, Requirements, FollowUpDate, AnticipatedDealValue, Probability, ExpectedClosingDate, Status, NextStep, Emp, Rev, AddressLine1, AddressLine2, Country, States, City, Zip, Mobile,Fax
}
var prospectName:String?, visitDate:String? , visitType:String?, industry:String?, source:String?, dissSummary:String?, requirements:String?, followUpDate:String?, anticipatedDealValue:String?, probability:String?, expectedClosingDate:String?, status:String?, nextStep:String?, emp:String?, rev:String?, addressLine1:String?, addressLine2:String?, country, statesVal:String?, cityVal:String?, zip:String?, mobile:String?,fax:String?
var currentLeadMaster:WizLeadMaster?
// MARK: - Section Data Structure
struct Section {
var name: String!
var options: [Any]!
var collapsed: Bool!
var optionItem:[Any]!
var icon:UIImage!
init(name: String, options: [Any], collapsed: Bool = false,optionItem:[Any],icon:UIImage) {
self.name = name
self.options = options
self.collapsed = collapsed
self.optionItem = optionItem
self.icon = icon
}
}
// MARK: - Properties
#IBOutlet weak var tblLeadDetail:UITableView?
var dataSource:NSArray = NSArray(objects: "Lead Summary","Address","Products","Contacts","Attachment","Follow Up","Remarks")
var sections = [Section]()
lazy var visitArr:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMVisittype)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var leadStatus:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMLeadStatus)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var leadSource:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMLeadsource)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var industries:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMCustomerType)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var countries:NSArray = {
let visitTypes = Handler.countriesList()
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizCountryList
let dict = ["system":(visitModel.lable! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
lazy var states:NSArray = {
let visitTypes = Handler.fetchLookUpOftype(type: LookUpType.enum_cRMTerritory)
var optionsArray = NSMutableArray(capacity: 0)
for visitType in visitTypes {
let visitModel = visitType as! WizLookUp
let dict = ["system":(visitModel.system! as String),"value":(visitModel.value! as String)]
optionsArray.add(dict as NSDictionary)
}
return optionsArray as NSArray
}()
func setStatesForCountry(_ country:String) -> Void{
}
func filterValue(_ arr:NSArray,key:String) -> Any {
let predicate = NSPredicate(format: "self == %#", key)
let filteredArr = arr.filtered(using: predicate)
if (filteredArr.count == 0) {
return ""
}
return filteredArr[0]
}
// MARK: - ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
let barButtonItem = UIBarButtonItem(image:UIImage(named:"save1") , style: UIBarButtonItemStyle.done, target: self, action: #selector(saveLeadInfo))
navigationItem.rightBarButtonItem = barButtonItem
sections = [
Section(name: "Lead Summary", options: [["title":"Prospect Name","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.customer_name as Any,"tag":TextFieldTags.ProspectName.rawValue],
["title":"Visit Date","type":"date","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.visited_date as Any,"tag":TextFieldTags.VisitDate.rawValue],
["title":"Visit Type","type":"dropdown","options":visitArr.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.VisitType.rawValue],
["title":"Industry","type":"dropdown","options":industries.value(forKeyPath: "system"),"value":filterValue(industries.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.type)!),"tag":TextFieldTags.Industry.rawValue],
["title":"Source","type":"dropdown","options":leadSource.value(forKeyPath: "system"),"value":filterValue(leadSource.value(forKeyPath: "value") as! NSArray , key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.source)!),"tag":TextFieldTags.Source.rawValue],
["title":"Discussion Summary","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.summary_disc as Any,"tag":TextFieldTags.DissSummary.rawValue],
["title":"Requirements","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.requirements as Any ,"tag":TextFieldTags.Requirements.rawValue],
["title":"Follow-Up Date","type":"date","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.followup_date as Any,"tag":TextFieldTags.FollowUpDate.rawValue],
["title":"Anticipated Deal Value","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.anticipated_deal_value as Any,"tag":TextFieldTags.AnticipatedDealValue.rawValue],
["title":"Probabilty","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.probability as Any,"tag":TextFieldTags.Probability.rawValue],
["title":"Expected Closing Date","type":"date","value":currentLeadMaster?.expected_closing_date as Any,"tag":TextFieldTags.ExpectedClosingDate.rawValue],
["title":"Status","type":"dropdown","options":leadStatus.value(forKeyPath: "system"),"value":filterValue(leadStatus.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.status)!),"tag":TextFieldTags.Status.rawValue],
["title":"Next Step","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.next_activity_planned as Any,"tag":TextFieldTags.NextStep.rawValue],
["title":"Emp","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.no_of_emp as Any,"tag":TextFieldTags.Emp.rawValue],
["title":"Rev","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.annual_revenue as Any,"tag":TextFieldTags.Rev.rawValue]], collapsed: true, optionItem: [],icon:UIColor.clear.getImage(size: CGSize(width: 30, height: 30))),
Section(name: "Address", options: [["title":"Address Line 1","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline1 as Any ,"tag":TextFieldTags.AddressLine1.rawValue],
["title":"Address Line 2","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.AddressLine2.rawValue],
["title":"Select Country","type":"dropdown","options":countries.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.Country.rawValue],
["title":"Select States","type":"dropdown","options":visitArr.value(forKeyPath: "system") as! Array<String>,"value":filterValue(visitArr.value(forKeyPath: "value") as! NSArray, key: ((currentLeadMaster == nil) ? "":currentLeadMaster?.visit_type)!),"tag":TextFieldTags.States.rawValue],
["title":"Select City","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.City.rawValue],
["title":"Zip Code","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Zip.rawValue],
["title":"Mobile","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Mobile.rawValue],
["title":"Fax","type":"input","value":(currentLeadMaster == nil) ? "":currentLeadMaster?.addressline2 as Any,"tag":TextFieldTags.Fax.rawValue]
], collapsed: true, optionItem: [],icon:UIColor.clear.getImage(size: CGSize(width: 30, height: 30))),
Section(name: "Product", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "add")!),
Section(name: "Contacts", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "add")!),
Section(name: "Attachment", options: [], collapsed: true, optionItem: [],icon:UIImage.init(named: "attach")!),
]
let headerNib = UINib(nibName: "WizHeaderView", bundle: nil)
tblLeadDetail?.register(headerNib, forHeaderFooterViewReuseIdentifier: "headerLeads")
let textInputCell = UINib(nibName: "TextInputCell", bundle: nil)
tblLeadDetail?.register(textInputCell, forCellReuseIdentifier: "textinputcell")
let dropDownInputCell = UINib(nibName: "DropDownInputCell", bundle: nil)
tblLeadDetail?.register(dropDownInputCell, forCellReuseIdentifier: "dropDownInputCell")
let dropDownDateCell = UINib(nibName: "DropDownDateCell", bundle: nil)
tblLeadDetail?.register(dropDownDateCell, forCellReuseIdentifier: "dropDownDateCell")
tblLeadDetail?.reloadData()
}
func saveLeadInfo() -> Void {
}
// MARK: - TableView Delegates & Datasource
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let optionsArray = sections[section].options
return optionsArray!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let optionsArray = sections[indexPath.section].options
let dict = optionsArray?[indexPath.row] as! Dictionary<String,Any>
if (dict["type"] as! String == "dropdown") {
let cell = tableView.dequeueReusableCell(withIdentifier: "dropDownInputCell") as! DropDownInputCell
cell.txtInput.isOptionalDropDown = false
cell.txtInput.itemList = dict["options"] as! [String]
cell.lblText.text = dict["title"] as? String
cell.txtInput.selectedItem = dict["value"] as? String
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.backgroundColor = UIColor.clear
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.delegate = self
return cell
}
else if (dict["type"] as! String == "input") {
let cell = tableView.dequeueReusableCell(withIdentifier: "textinputcell") as! TextInputCell
cell.lblText.text = dict["title"] as? String
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.backgroundColor = UIColor.clear
cell.txtInput.text = dict["value"] as? String
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.delegate = self
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "dropDownDateCell") as! DropDownDateCell
cell.lblText.text = dict["title"] as? String
cell.txtInput.dropDownMode = IQDropDownMode.datePicker
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
}
else{
cell.txtInput.selectedItem = dict["value"] as? String
}
cell.txtInput.tag = (dict["tag"] as? NSInteger)!
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.backgroundColor = UIColor.clear
cell.delegate = self
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// if indexPath.row == 0 {
// performSegue(withIdentifier: "leadsummary", sender:nil)
// }
// else{
// performSegue(withIdentifier: "leadaddress", sender:nil)
// }
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerLeads") as? WizHeaderView ?? WizHeaderView(reuseIdentifier: "headerLeads")
header.delegate = self
header.lblLeadSummary.text = sections[section].name
header.btnArrowButton.setImage(sections[section].icon, for: UIControlState.normal)
header.setCollapsed(sections[section].collapsed)
header.section = section
return header
}
func toggleSection(_ header: WizHeaderView, section: Int) {
let collapsed = !sections[section].collapsed
// Toggle collapse
sections[section].collapsed = collapsed
header.setCollapsed(collapsed)
// Adjust the height of the rows inside the section
tblLeadDetail?.beginUpdates()
for i in 0 ..< sections[section].options.count {
tblLeadDetail?.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
}
tblLeadDetail?.endUpdates()
}
#IBAction func placeOrder(_ sender: Any) {
let placeOrder = MainStoryBoard.instantiateViewController(withIdentifier: "PlaceOrder")
navigationController?.pushViewController(placeOrder, animated: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 35.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10.0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return sections[(indexPath as NSIndexPath).section].collapsed! ? 0 : 70.0
}
func cellTextField(_with TextField: IQDropDownTextField, didSelectItem item: String?) {
switch TextField.tag {
case TextFieldTags.VisitType.rawValue:
visitType = item
break
case TextFieldTags.Industry.rawValue:
industry = item
break
case TextFieldTags.Source.rawValue:
source = item
break
case TextFieldTags.Status.rawValue:
status = item
break
case TextFieldTags.Country.rawValue:
country = item
break
case TextFieldTags.States.rawValue:
statesVal = item
break
default: break
}
}
func cellTextFieldDidEndEditing(_with textField: UITextField) {
switch textField.tag {
case TextFieldTags.ProspectName.rawValue:
prospectName = textField.text
break
case TextFieldTags.DissSummary.rawValue:
dissSummary = textField.text
break
case TextFieldTags.Requirements.rawValue:
requirements = textField.text
break
case TextFieldTags.AnticipatedDealValue.rawValue:
anticipatedDealValue = textField.text
break
case TextFieldTags.Probability.rawValue:
probability = textField.text
break
case TextFieldTags.NextStep.rawValue:
nextStep = textField.text
break
case TextFieldTags.Emp.rawValue:
emp = textField.text
break
case TextFieldTags.Rev.rawValue:
rev = textField.text
break
case TextFieldTags.AddressLine1.rawValue:
addressLine1 = textField.text
break
case TextFieldTags.AddressLine2.rawValue:
addressLine2 = textField.text
break
case TextFieldTags.City.rawValue:
cityVal = textField.text
break
case TextFieldTags.Mobile.rawValue:
mobile = textField.text
break
case TextFieldTags.Fax.rawValue:
fax = textField.text
break
case TextFieldTags.Zip.rawValue:
zip = textField.text
break
default: break
}
}
func stringFromDate(_ date:Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: date)
}
func cellTextFieldDate(_with textField: IQDropDownTextField, didSelect date: Date?) {
switch textField.tag {
case TextFieldTags.VisitDate.rawValue:
visitDate = stringFromDate(date!)
break
case TextFieldTags.FollowUpDate.rawValue:
followUpDate = stringFromDate(date!)
break
case TextFieldTags.ExpectedClosingDate.rawValue:
expectedClosingDate = stringFromDate(date!)
break
default: break
}
}
}
extension UIColor {
func getImage(size: CGSize) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image(actions: { rendererContext in
self.setFill()
rendererContext.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
})
}}
While using UITableView with reusable cell identifier, you have to store input of your UITextField or any other input fields when editing is finished into your data source which you are using to fill all cells. Because reusable cell manage memory itself to draw all your controls, so there are no guaranty about your data remains as it is when you scroll your tableview.
In this if - else expression
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
} else{
cell.txtInput.selectedItem = dict["value"] as? String
}
you are setting two different UI elements depending on the dictionary value.
As cells are reused those UI states will remain until they are changed the next time.
You have to make sure that every UI element is set to a defined state to avoid unexpected behavior, so you need to set the other UI element, too. For example ([default value] is just a placeholder).
if dict["value"] as? String == "" {
cell.txtInput.setDate(Date(), animated: true)
cell.txtInput.selectedItem = [default value]
} else{
cell.txtInput.setDate([default value], animated: [default value])
cell.txtInput.selectedItem = dict["value"] as? String
}
If there is no unique default value you have to add properties to the data source to keep the current states of the date and the selected item.
Regarding the text field you have to update the data source after changing the text because in cellForRow the text field is always updated from the data source.
And - as always - don't use NS(Mutable)Array and NS(Mutable)Dictionary in Swift. You throw away the important type information and the mutable collection types are not related to the Swift native types. Finally you can delete all break lines in the switch expressions (except after default). They are redundant in Swift.
I'm developing an app where users writes entries to different topics then can give up and down points to the entries. I used in my tableViewController the function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
and I added these two lines at the end of this function:
cell.plusButton.tag = indexPath.row
cell.minusButton.tag = indexPath.row
So this should give every button in the tableView a tag so that its the same as indexpath.row of that cell, am I wrong? Because when I then try to call the buttons, all of their tags are same and equals to 0. How can I give them different tags? Is there no way to do so in this way?
This is what the code is when I want to call the button:
#IBAction func plus(sender: AnyObject) {
print(self.tag)
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+entriesArrayTwo[self.tag])
var value = Int()
var date = String()
var user = String()
var votedDown = [""]
var votedUp = [""]
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
let dict = snapshot.value as! [String: AnyObject]
value = dict["point"] as! Int
date = String(dict["date"]!)
user = String(dict["user"]!)
votedUp = dict["votedUp"] as! NSArray as! [String]
votedDown = dict["votedDown"] as! NSArray as! [String]
var tempBool = false
var temp = -1
for uid in votedDown {
temp = temp + 1
if uid == FIRAuth.auth()?.currentUser?.uid {
votedDown.removeAtIndex(temp)
tempBool = true
}
}
if tempBool == false {
votedUp.append((FIRAuth.auth()?.currentUser?.uid)!)
}
ref.setValue(["point": value+1, "date": date, "user": user, "votedDown": votedDown, "votedUp": votedUp])
self.point.text = String(value+1)
})
if minusButton.hidden == true {
minusButton.hidden = false
} else {
plusButton.hidden = true
}
}
My tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell function is below:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "entryCell", for: indexPath) as! HubEntryTableViewCell
if self.resultSearchController.isActive {
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+filteredTableData[(indexPath as NSIndexPath).row])
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
cell.point.text = String(describing: value!["point"]!)
let postRef = FIRDatabase.database().reference().child("users/"+String(describing: value!["user"]!))
postRef.observeSingleEvent(of: .value, with: { snapshotTwo in
let valueTwo = snapshotTwo.value as? NSDictionary
cell.subInfo.text = String(describing: valueTwo!["name"]!)+" "+String(describing: valueTwo!["surname"]!)+" - "+String(describing: value!["date"]!)
})
})
cell.entry.text = self.filteredTableData[(indexPath as NSIndexPath).row]
} else {
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+entriesArray[(indexPath as NSIndexPath).row])
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
cell.point.text = String(describing: value!["point"]!)
let postRef = FIRDatabase.database().reference().child("users/"+String(describing: value!["user"]!))
postRef.observeSingleEvent(of: .value, with: { snapshotTwo in
let valueTwo = snapshotTwo.value as? NSDictionary
cell.subInfo.text = String(describing: valueTwo!["name"]!)+" "+String(describing: valueTwo!["surname"]!)+" - "+String(describing: value!["date"]!)
})
let votedUpRef = ref.child("votedUp")
votedUpRef.observeSingleEvent(of: .value, with: { upSnapshot in
var tempDict = snapshot.value as! [String: AnyObject]
let tempArray = tempDict["votedUp"] as! [String]
for uid in tempArray {
if String(uid) == FIRAuth.auth()?.currentUser?.uid {
cell.plusButton.isHidden = true
}
}
})
let votedDownRef = ref.child("votedDown")
votedUpRef.observeSingleEvent(of: .value, with: { upSnapshot in
var tempDict = snapshot.value as! [String: AnyObject]
let tempArray = tempDict["votedDown"] as! [String]
for uid in tempArray {
if String(uid) == FIRAuth.auth()?.currentUser?.uid {
cell.minusButton.isHidden = true
}
}
})
})
cell.entry.text = self.entriesArray[(indexPath as NSIndexPath).row]
}
cell.plusButton.tag = (indexPath as NSIndexPath).row
cell.minusButton.tag = (indexPath as NSIndexPath).row
// NEW METHOD TO GET THE BUTTON
let check1: UIButton = (cell.viewWithTag(1) as! UIButton)
let check2: UIButton = (cell.viewWithTag(2) as! UIButton)
check1.addTarget(self, action: #selector(HubEntriesTableViewController.CloseMethod(_:event:)), for: .touchDown)
check2.addTarget(self, action: #selector(HubEntriesTableViewController.CloseMethod1(_:event:)), for: .touchDown)
// Configure the cell...
return cell
}
Perhaps finally found an issue. When I reproduced the problem in my project, I realised that downcasting to UIButton was missing.
So within HubEntryTableViewCell subclass update the method like this:
#IBAction func plus(sender: AnyObject) {
// self.tag, if called from UITableViewCell subclass, is rather cell's tag, not button's tag
let button = sender as! UIButton
print("button.tag = \(button.tag)")
...
}
If you have perform action on button click in tableview then try this code you don't worry about tag ..
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Eventcell", forIndexPath: indexPath)
let check1: UIButton = (cell.viewWithTag(11) as! UIButton)
let check2: UIButton = (cell.viewWithTag(12) as! UIButton)
check1.addTarget(self, action: #selector(EventListController.CloseMethod(_:event:)), forControlEvents: .TouchDown)
check2.addTarget(self, action: #selector(EventListController.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
#IBAction func CloseMethod(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.Eventlisttable)
let indexPath = self.Eventlisttable.indexPathForRowAtPoint(currentTouchPosition)!
print("\(Int(indexPath.row))")
}
#IBAction func CloseMethod1(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.Eventlisttable)
let indexPath = self.Eventlisttable.indexPathForRowAtPoint(currentTouchPosition)!
print("\(Int(indexPath.row))")
}
If my answer help you then give vote. thank you..
I'm working on iOS application using swift and firebase:
I tried to add search bar to a table view as the following code:
import UIKit
class MyTableVC: UITableViewController,UISearchResultsUpdating {
var resultSearchController = UISearchController(searchResultsController: nil)
//var filteredUsers = [nsdi]()
var filteredUsers: NSMutableArray = []
var UserNamesArray: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
// self.resultSearchController = UISearchController(searchResultsController: nil)
// self.resultSearchController.searchResultsUpdater = self
// self.resultSearchController.dimsBackgroundDuringPresentation = false
// //self.resultSearchController.searchBar.sizeThatFits()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
let ref = Firebase(url: "https://hissah-1st-fb-test.firebaseio.com/Users")
ref.queryOrderedByChild("Job").queryEqualToValue("Programs")
.observeEventType(.Value, withBlock: { snapshot in
if let dict = snapshot.value as? NSMutableDictionary{
//print("dict====== \(dict)")
for (key,value) in dict {
let mainDict = NSMutableDictionary()
mainDict.setObject(key, forKey: "userid")
if let dictnew = value as? NSMutableDictionary{
if let metname = dictnew["UserName"] as? String
{
mainDict.setObject(metname, forKey: "UserName")
}
if let metname = dictnew["Details"] as? String
{
mainDict.setObject(metname, forKey: "Details")
}
if let metname = dictnew["Email"] as? String
{
mainDict.setObject(metname, forKey: "Email")
}
}
//print("mainDict========= \(mainDict)")
self.UserNamesArray.addObject(mainDict)
}
//print("UserNamesArray ==== \(self.UserNamesArray)")
}
self.tableView.reloadData()
})
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.resultSearchController.active
{
return self.filteredUsers.count
}else
{
return UserNamesArray.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UserCell", forIndexPath: indexPath) as UITableViewCell
if self.resultSearchController.active
{
// cell.textLabel?.text = self.filteredUsers[indexPath.row] as? String
if let name = self.filteredUsers[indexPath.row] as? NSMutableDictionary{
cell.textLabel?.text = name["UserName"] as? String
cell.detailTextLabel?.text = name["Details"] as? String
}
}
else{
if let name = UserNamesArray[indexPath.row] as? NSMutableDictionary{
cell.textLabel?.text = name["UserName"] as? String
cell.detailTextLabel?.text = name["Details"] as? String
}
}
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
let detailsViewController = self.storyboard!.instantiateViewControllerWithIdentifier("DetailsViewController") as! DetailsVC
if let name = self.UserNamesArray[indexPath.row] as? NSMutableDictionary{
detailsViewController.self.strUserid = name["userid"] as? String
}
self.navigationController?.pushViewController(detailsViewController, animated: true)
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredUsers.removeAllObjects()
//attributeA contains[cd] $A OR attributeB contains[cd]
let searchPredicate = NSPredicate(format: "UserName contains[cd] %# OR Details contains[cd] %#", searchController.searchBar.text!,searchController.searchBar.text!)
let array = (UserNamesArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
for type in array {
// Do something
filteredUsers .addObject(type)
}
// filteredUsers = array as! [String]
self.tableView.reloadData()
}
}
Here's the table view:
When the user clicks at any item, it retreives the information for that item from firebase, if I clicked on Sara for example, it gives her name and her email as here:
When I search for example for mariah or hello, it gives the right result, as here:
But if I clicked on the result Item, it always retrieves the information of the first item!'Sara's Information', for example the result mariah, gives this:
Can anyone tell me, how can I fix this? and why is this happing?
I found that you are not setting return array of search result at didSelectRowAtIndexPath. so you set didSelectRowAtIndexPath code like following:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if self.resultSearchController.active
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
let detailsViewController = self.storyboard!.instantiateViewControllerWithIdentifier("DetailsViewControllerroller") as! DetailsViewController
if let name = self.filteredUsers[indexPath.row] as? NSMutableDictionary{
detailsViewController.self.strUserid = name["userid"] as? String
}
self.navigationController?.pushViewController(detailsViewController, animated: true)
}
}
else
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
let detailsViewController = self.storyboard!.instantiateViewControllerWithIdentifier("DetailsViewControllerroller") as! DetailsViewController
if let name = self.UserNamesArray[indexPath.row] as? NSMutableDictionary{
detailsViewController.self.strUserid = name["userid"] as? String
}
self.navigationController?.pushViewController(detailsViewController, animated: true)
}
}
In your cellForRow you are checking if user is actively searching and filtering result
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if self.resultSearchController.active
{
if let name = self.filteredUsers[indexPath.row] as? NSMutableDictionary{
// other code
}
else{
..// here get data from original array
if let name = UserNamesArray[indexPath.row] as? NSMutableDictionary{
return cell
}
However while in didSelectRow method you are not using the same flow
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let name = self.UserNamesArray[indexPath.row] as? NSMutableDictionary{
This is why you are getting wrong result.
you need to also check if the searchController is active here
if self.resultSearchController.active
{
//get from filteredUsers
}
else{
//get from original users
}