I have a table view with two different prototype cells.
The second prototype cell has a custom Label, but when I execute the project it's showing nothing. The text is there (I know because it's printing) but it's not appearing.
TableView Storyboard
Simulator: Image cell ok, but text cell is empty
-> TableViewController:
import UIKit
import Twitter
class TweetMentionsTableViewController: UITableViewController
{
lazy var images: [MediaItem] = []
lazy var userMentions: [Mention]? = []
lazy var hashtags: [Mention]? = []
lazy var urls: [Mention]? = []
var tweet: Twitter.Tweet?
private enum MentionTypes {
case Images
case Hashtags
case Users
case URLs
}
private var mentionsCollection: Dictionary<MentionTypes, Bool> = [
MentionTypes.Images: false,
MentionTypes.Hashtags: false,
MentionTypes.Users: false,
MentionTypes.URLs: false
]
private var sectionTypes: Dictionary<MentionTypes, Int?> = [
MentionTypes.Images: nil,
MentionTypes.Hashtags: nil,
MentionTypes.Users: nil,
MentionTypes.URLs: nil
]
override func numberOfSections(in tableView: UITableView) -> Int {
var count = 0
if !(tweet?.media.isEmpty)! {
mentionsCollection[MentionTypes.Images] = true
count += 1
}
if !(tweet?.hashtags.isEmpty)! {
mentionsCollection[MentionTypes.Hashtags] = true
count += 1
}
if !(tweet?.userMentions.isEmpty)! {
mentionsCollection[MentionTypes.Users] = true
count += 1
}
if !(tweet?.urls.isEmpty)! {
mentionsCollection[MentionTypes.URLs] = true
count += 1
}
return count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if mentionsCollection[MentionTypes.Images] == true {
sectionTypes[MentionTypes.Images] = section
mentionsCollection[MentionTypes.Images] = false
return (tweet?.media.count)!
} else if mentionsCollection[MentionTypes.Hashtags] == true {
sectionTypes[MentionTypes.Hashtags] = section
mentionsCollection[MentionTypes.Hashtags] = false
return (tweet?.hashtags.count)!
} else if mentionsCollection[MentionTypes.Users] == true {
sectionTypes[MentionTypes.Users] = section
mentionsCollection[MentionTypes.Users] = false
return (tweet?.userMentions.count)!
} else if mentionsCollection[MentionTypes.URLs] == true {
sectionTypes[MentionTypes.URLs] = section
mentionsCollection[MentionTypes.URLs] = false
return (tweet?.urls.count)!
}
return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let data = temp[indexPath.row][indexPath.section]
let cell = tableView.dequeueReusableCell(withIdentifier: "TweetMention", for: indexPath)
// Configure the cell...
if let sec = sectionTypes[MentionTypes.Images], indexPath.section == sec {
let image = tweet?.media[indexPath.row].url
if let tweetMentionCell = cell as? TweetMentionsTableViewCell {
tweetMentionCell.imageURL = image!
return cell
}
}
let cell2 = tableView.dequeueReusableCell(withIdentifier: "TweetMention2Cell", for: indexPath)
if let sec = sectionTypes[MentionTypes.Hashtags], indexPath.section == sec {
let hashtag = tweet?.hashtags[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.hashtag = (hashtag?.keyword)!
}
} else if let sec = sectionTypes[MentionTypes.Users], indexPath.section == sec {
let userMention = tweet?.userMentions[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.userMention = (userMention?.keyword)!
}
} else if let sec = sectionTypes[MentionTypes.URLs], indexPath.section == sec {
let url = tweet?.urls[indexPath.row]
if let tweetMentionCell = cell2 as? TweetMentions2TableViewCell {
tweetMentionCell.url = (url?.keyword)!
}
}
return cell2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if let sec = sectionTypes[MentionTypes.Images], section == sec {
return "Images"
} else if let sec = sectionTypes[MentionTypes.Hashtags], section == sec {
return "Hashtags"
} else if let sec = sectionTypes[MentionTypes.Users], section == sec {
return "Users"
} else if let sec = sectionTypes[MentionTypes.URLs], section == sec {
return "URLs"
}
return nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let cell = sender as? TweetMentionsTableViewCell {
if segue.identifier == "Show Image" {
if let imageVC = segue.destination as? ImageViewController {
imageVC.imageURL = cell.imageURL?.absoluteURL
//imageVC.imageRatio = tweet?.media[0].aspectRatio
}
}
}
if let cell = sender as? TweetMentions2TableViewCell {
if segue.identifier == "Show Text" {
if let mentionsSTTVC = segue.destination as? MentionsSearchTweetTableViewController {
if let text = cell.mentLabel {
mentionsSTTVC.mentionToSearch = text.text!
}
}
}
}
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if let cell = sender as? TweetMentions2TableViewCell {
if (cell.url.contains("http")) {
UIApplication.shared.open(NSURL(string: cell.url)! as URL)
return false
}
}
return true
}
}
-> TableViewCell:
import UIKit
class TweetMentions2TableViewCell: UITableViewCell
{
#IBOutlet weak var mentLabel: UILabel!
var userMention = "" {
didSet {
updateUI(mentionType: 2)
}
}
var hashtag = "" {
didSet {
updateUI(mentionType: 1)
}
}
var url = "" {
didSet {
updateUI(mentionType: 3)
}
}
func updateUI(mentionType: Int) {
if mentionType == 1 {
mentLabel.text = hashtag
} else if mentionType == 2 {
mentLabel.text = userMention
} else if mentionType == 3 {
mentLabel.text = url
}
print(mentLabel.text!)
}
}
Related
I have a problem with passing the data
To make it clear, I will explain the idea of what I worked on, and what is the problem.
The idea is in the first view controller the user will enter the title and description and then chooses from the options of the pop-up button, the options are (exchange, borrow, donation, sell). The data entered will be saved in the option chosen by the user. then the data will be displayed in the second view controller in the table view. If the user chooses the exchange option and enters the data, his data will be displayed in the table view in the exchange (index 0) and this works for me the data is displayed in the table view in the correct form as I want.
The problem I am experiencing is when I pass the data to the other view controller.
When the user clicks on any cell, it will pass the same data regardless of the difference in the index. If the user chooses the borrow (index 1) and clicks any cell, it'll display the exchange (index 0) data. No matter what indexes you choose and the cell you click on it will pass the same data!!!!!
first view controller
here I'm entering the data
it's shown in the table view in the right index of the segment no problem with that
after I click it pass the right data
look here if I change the index and click to any cell it will pass the same data!!
look here if I change the index and click to any cell it will pass the same data!!
Here's my code for the first vc
import UIKit
import FirebaseFirestore
class ViewController4: UIViewController {
#IBOutlet weak var mssglabel: UILabel!
#IBOutlet weak var selectservice: UIButton!
#IBOutlet weak var titleTextField: UITextField!
#IBOutlet weak var descriptionTextField: UITextView!
#IBOutlet weak var custombtun: UIButton!
let db = Firestore.firestore()
var chooseOption = ""
override func viewDidLoad() {
super.viewDidLoad()
setpopupbutn()
selectservice.layer.cornerRadius = 25
descriptionTextField.layer.cornerRadius = 25
custombtun.layer.cornerRadius = 25
}
#IBAction func containbutn(_ sender: Any) {
let vc = (storyboard?.instantiateViewController(withIdentifier: "vc3"))!
navigationController?.pushViewController(vc, animated: true)
spcificOption()
}
func saveDataDonation() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userDonationDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle)
}
} // end of closure
}
}
func saveDataSale() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text{
// Save Data to Database
db.collection("userSaleDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataExchange() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userExchangeDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle ]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func saveDataBorrow() {
if let description = descriptionTextField.text,
let tittle = titleTextField.text {
// Save Data to Database
db.collection("userBorrowDatabase")
.addDocument(data: [
"description" : description,
"BookTitle": tittle]) {
(error) in
if let err = error {
print(err.localizedDescription)
}else {
print("تم حفظ البيانات بنجاح")
print(description)
print(tittle) }
}
}
}
func setpopupbutn () {
let option = {( ACTION : UIAction ) in
self.chooseOption = ACTION.title
print("حفظ الداتا في ",self.chooseOption)}
selectservice.menu = UIMenu (children : [
UIAction (title : "تبرع" , state: .on , handler: option),
UIAction (title : "بيع" , handler: option),
UIAction (title : "تبادل" , handler: option),
UIAction (title : "إستعارة" , handler: option),
])
saveDataDonation()
selectservice.showsMenuAsPrimaryAction = true
selectservice.changesSelectionAsPrimaryAction = true
}
func spcificOption() {
if chooseOption == ("تبرع") {
saveDataDonation()
} else if chooseOption == ("بيع") {
saveDataSale()
} else if chooseOption == ("تبادل") {
saveDataExchange()
} else if chooseOption == ("إستعارة") {
saveDataBorrow()
}
}
}
and this is the second vc (Table view)
import UIKit
import FirebaseFirestore
import Firebase
class ViewController3: UIViewController, UITableViewDataSource, UITableViewDelegate {
let db = Firestore.firestore()
var exchange : [exchange] = []
var borrow : [borrow] = []
var donation : [donation] = []
var sale : [sale] = []
#IBOutlet weak var segmentOutlet: UISegmentedControl!
#IBOutlet weak var userDataTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
userDataTableView.dataSource = self
userDataTableView.delegate = self
getDataDonation()
getDataSale()
getDataExchange()
getDataBorrow()
userDataTableView.reloadData()
}
#IBAction func serviceSeg(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
getDataExchange()
}
else if sender.selectedSegmentIndex == 1 {
getDataBorrow()
}
else if sender.selectedSegmentIndex == 2 {
getDataDonation()
}
else if sender.selectedSegmentIndex == 3 {
getDataSale()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
func getDataDonation(){
donation.removeAll()
db.collection("userDonationDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.donation.append(finalProject.donation(passTitle3:data["BookTitle"] as! String , passDes3: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataSale(){
sale.removeAll()
db.collection("userSaleDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.sale.append(finalProject.sale(passTitle4:data["BookTitle"] as! String , passDes4: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataExchange(){
exchange.removeAll()
db.collection("userExchangeDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.exchange.append(finalProject.exchange(passTitle1:data["BookTitle"] as! String , passDes1: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func getDataBorrow(){
borrow.removeAll()
db.collection("userBorrowDatabase")
.getDocuments { querySnapshot, error in
if let err = error { print(err.localizedDescription)}
else {
for document in querySnapshot!.documents {
let data = document.data()
print( data["BookTitle"] as! String )
self.borrow.append(finalProject.borrow(passTitle2:data["BookTitle"] as! String , passDes2: data["description"] as! String))
}
DispatchQueue.main.async {
self.userDataTableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
Note... I tried to do this but it didn't work
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
vc.recivedB = borrow[indexPath.row]
vc.recivedD = donation[indexPath.row]
vc.recivedS = sale[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
This is the struct,
I created a struct for each index
public struct exchange {
var passTitle : String
var passDes : String
init (passTitle1:String, passDes1:String) {
self.passTitle = passTitle1
self.passDes = passDes1
}
}
public struct borrow {
var passTitle : String
var passDes : String
init (passTitle2:String, passDes2:String) {
self.passTitle = passTitle2
self.passDes = passDes2
}
}
public struct donation {
var passTitle : String
var passDes : String
init (passTitle3:String, passDes3:String) {
self.passTitle = passTitle3
self.passDes = passDes3
}
}
public struct sale {
var passTitle : String
var passDes : String
init (passTitle4:String, passDes4:String) {
self.passTitle = passTitle4
self.passDes = passDes4
}
}
this is the last vc
import UIKit
import FirebaseStorage
import Firebase
import FirebaseFirestore
import SDWebImage
class ViewController10: UIViewController {
#IBOutlet weak var userBookTitle: UILabel!
#IBOutlet weak var userBookDescription: UILabel!
var recivedE:exchange?
var recivedB:borrow?
var recivedD:donation?
var recivedS:sale?
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
}
}
Note... I tried to do this but it didn't work
override func viewDidLoad() {
super.viewDidLoad()
if let et = recivedE?.passTitle ,
let ed = recivedE?.passDes{
userBookTitle.text = et
userBookDescription.text = ed
}
else if let bt = recivedB?.passTitle ,
let bd = recivedB?.passDes {
userBookTitle.text = bt
userBookDescription.text = bd
}
else if let dt = recivedD?.passTitle ,
let dd = recivedD?.passDes {
userBookTitle.text = dt
userBookDescription.text = dd
}
else if let st = recivedS?.passTitle ,
let sd = recivedS?.passDes {
userBookTitle.text = st
userBookDescription.text = sd
}
}
and this also not working
override func viewDidLoad() {
super.viewDidLoad()
userBookTitle.text = recivedE?.passTitle
userBookDescription.text = recivedE?.passDes
userBookTitle.text = recivedB?.passTitle
userBookDescription.text = recivedB?.passDes
userBookTitle.text = recivedD?.passTitle
userBookDescription.text = recivedD?.passDes
userBookTitle.text = recivedS?.passTitle
userBookDescription.text = recivedS?.passDes
}
help me, please
In both of your numberOfRowsInSection and cellForRowAt functions, you are checking the selected segment index to determine which data to use:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentOutlet.selectedSegmentIndex == 0 {
return exchange.count
} else if segmentOutlet.selectedSegmentIndex == 1 {
return borrow.count
}else if segmentOutlet.selectedSegmentIndex == 2 {
return donation.count
} else if segmentOutlet.selectedSegmentIndex == 3 {
return sale.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if segmentOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = exchange [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 1 {
cell.textLabel?.text = borrow [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 2 {
cell.textLabel?.text = donation [indexPath.row].passTitle
} else if segmentOutlet.selectedSegmentIndex == 3 {
cell.textLabel?.text = sale [indexPath.row].passTitle
}
return cell
}
However, in didSelectRowAt, you only use the exchange data:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier:"vc10") as? ViewController10 {
vc.recivedE = exchange[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
If you implement the same if / else if structure in didSelectRowAt you should get the desired results.
Greetings I have the disadvantage that the cells of the tableview are duplicated in a random way, at certain times the event occurs, not always
i think i need to clean or reload data but im not sure, could you help please.
final class MovementsDatasource: NSObject, PaginatedDatasource {
typealias Values = MovementsSectionModel
private let repository: TransactionsRepositoryProtocol
private let disposeBag = DisposeBag()
private let fetching = ActivityIndicator()
private let id: String, cvl: String
private let rowsObserver = PublishSubject<[Values]>()
var values = [Values]() { didSet { self.rowsObserver.onNext(self.values) } }
var page = 0
var hasNextPage = false
init(repository: TransactionsRepositoryProtocol, id: String, cvl: String) {
self.id = id
self.cvl = cvl
self.repository = repository
}
func getActivityIndicator() -> ActivityIndicator { self.fetching }
func getValuesObserver() -> Observable<[Values]> { self.rowsObserver }
func getNextPage() {
guard self.hasNextPage else { return }
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
func getFirstPage() {
self.page = 0
self.hasNextPage = false
self.values = []
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
}
extension MovementsDatasource {
private func getMovements(for productID: String, cvl: String, page: Int) {
self.repository
.movements(userCVL: cvl, ibanAccountId: productID, page: page)
.trackActivity(self.fetching)
.subscribe(onNext: { [weak self] response in
guard let _self = self else { return }
_self.hasNextPage = response.hasNextPage
_self.page = _self.hasNextPage ? (_self.page + 1) : _self.page
_self.publish(response)
})
.disposed(by: self.disposeBag)
}
private func publish(_ response: MovementsResponse) {
guard let rawMovement = response.values else { return }
let newRows = self.transform(rawMovement)
var rows = self.values
if !rows.isEmpty { rows += newRows }
else { rows = newRows }
self.values = self.merge(rows)
}
internal func transform(_ movements: [Movement]) -> [MovementsSectionModel] {
movements
.map { $0.movementDate.displayTimestamp() }
.unique()
.map { title -> MovementsSectionModel in
let sorted = movements.filter { $0.movementDate.displayTimestamp() == title }
return MovementsSectionModel(header: title, items: sorted)
}
}
internal func merge(_ sections: [MovementsSectionModel]) -> [MovementsSectionModel] {
sections
.map { $0.header }
.unique()
.map { title -> MovementsSectionModel in
let merged = sections.reduce(into: [MovementCellViewModel]()) {
accumulator, section in
if section.header == title { accumulator += section.items }
}
return MovementsSectionModel(header: title, items: merged)
}
}
}
extension MovementsDatasource {
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
self.values[section].items.count
}
func numberOfSections(in _: UITableView) -> Int { self.values.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard !self.values.isEmpty, let cell = tableView.dequeueReusableCell(withIdentifier: MovementCell.reuseID, for: indexPath) as? MovementCell
else {
return UITableViewCell(frame: .zero)
}
let section = indexPath.section
let index = indexPath.item
if !self.values.isEmpty {
cell.viewModel = self.values[section].items[index]
}
return cell
}
}
I am creating an app where I have a detailViewController where there is an address of place and when the user clicks on the address - it opens the apple maps (which what I want) but it does not show that router as a destination to go to that address. I am getting to: -118.277.... and from myLocation (which is good), except the to part & it also tells directions not available. So, I want the address that didSelectRow is pressed to bring the destination directions in apple maps when clicked. thank you for the help.
UPDATED I just debug and created a watch for place.location?.coordinate I see some result but they are just longitude and latitude. But, I want to show the address instead of lat & long.
Here is my code:
import UIKit
import Social
import CoreLocation
class DetailsViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var nearMeIndexSelected = NearMeIndexTitle()
var place: Place!
var coordinate :CLLocationCoordinate2D?
var nearMeRequest : NearMeRequest!
var locationManager = CLLocationManager()
let infoCellId = "info-cell"
let interactiveCellId = "interactive-cell"
let directionsSegueId = "directions-segue"
let gallerySegueId = "gallery-segue"
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var annotation: ARAnnotation!
override func viewDidLoad() {
super.viewDidLoad()
if self.place != nil {
self.title = place.placeName
self.tableView.delegate = self
self.tableView.dataSource = self
self.loadDetailInfo()
print("Place is not nil")
} else {
print("Place is nil")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 4
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 4
}
else if section == 1 {
return 0
}
else if section == 2 {
return 1
}
else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cellId = infoCellId
if indexPath.section != 0 {
cellId = interactiveCellId
}
let cell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellId)! as UITableViewCell
var key = "Not Available"
var value = "Not Available"
if indexPath.section == 0 {
if indexPath.row == 0 {
key = "Name"
if self.place.placeName.characters.count > 0 {
value = self.place.placeName
}
} else if indexPath.row == 1 {
key = "Address"
if let address = self.place.address {
if address.characters.count > 0 {
value = address
}
}
} else if indexPath.row == 2 {
key = "Phone number"
if let phoneNumber = self.place.phoneNumber {
if phoneNumber.characters.count > 0 {
value = phoneNumber
}
}
} else if indexPath.row == 3 {
key = "Website"
if let website = self.place.website {
if website.characters.count > 0 {
value = website
}
}
}
}
else if indexPath.section == 2 {
key = "Get Directions"
} else {
key = "Photos of \(self.place.placeName)"
}
if indexPath.section == 0 {
cell.textLabel?.text = key
cell.detailTextLabel?.text = value
} else {
cell.textLabel?.text = key
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if indexPath.row == 1 {
print("it works!")
if let coordinate = place.location?.coordinate {
if let url = URL(string:"http://maps.apple.com/maps?daddr=\(coordinate.longitude),\(coordinate.latitude)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
if indexPath.row == 2 {
guard let phoneNumber = self.place.phoneNumber,
phoneNumber.count > 0,
let url = URL(string: "tel:\(phoneNumber.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)")
else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
} else { return }
}
else if indexPath.section == 2 {
self.performSegue(withIdentifier: directionsSegueId, sender: self)
} else {
if let photoReferences = self.place.photoReferences {
if photoReferences.count != 0 {
self.performSegue(withIdentifier: gallerySegueId, sender: self)
return
}
}
// View Photo button
let alertController = UIAlertController(title: "No photos", message: "Sorry, but there are no photos found for this place.", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Ok", style: .cancel) { action in
// ...
}
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return " Info"
}
else if section == 1{
//return " Request a Uber"
return ""
}
else if section == 2 {
return " Navigation"
} else {
return " Photo"
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == directionsSegueId {
let controller = segue.destination as! DirectionsViewController
controller.place = self.place
} else if segue.identifier == gallerySegueId {
let navController = segue.destination as! UINavigationController
let galleryController = navController.viewControllers[0] as! GalleryViewController
galleryController.place = self.place
}
}
func generatePlaceUrl() -> URL {
let placeNamePlus = self.place.placeName.replacingOccurrences(of: " ", with: "+")
var urlString = "https://www.google.com/maps/place/" + placeNamePlus
if let addressPlus = self.place.address?.replacingOccurrences(of: " ", with: "+") {
urlString = urlString + "+" + addressPlus
}
let url = URL.init(string: urlString)
if let finalUrl = url {
return finalUrl
}
return URL.init(string: "https://www.maps.google.com")!
}
func loadDetailInfo() {
let loader = PlacesLoader()
self.activityIndicator.startAnimating()
self.tableView.isHidden = true
loader.loadDetailInformation(forPlace: self.place) { (dictionary, error) in
guard dictionary != nil else {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.tableView.isHidden = false
self.tableView.reloadData()
}
return
}
if let resultDictionary = dictionary!["result"] as? NSDictionary {
self.place.address = (resultDictionary["formatted_address"] as? String)!
self.place.phoneNumber = resultDictionary["formatted_phone_number"] as? String
self.place.website = resultDictionary["website"] as? String
if let photosArr = resultDictionary["photos"] as? [NSDictionary] {
for photoDict in photosArr {
let photoReference = photoDict["photo_reference"] as? String
if self.place.photoReferences == nil {
self.place.photoReferences = [String]()
}
self.place.photoReferences?.append(photoReference!)
}
}
}
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.tableView.isHidden = false
self.tableView.reloadData()
}
}
}
}
Your latitude and longitude values are the wrong way round, try this:
if let url = URL(string:"http://maps.apple.com/maps?daddr=\(coordinate.latitude),\(coordinate.longitude)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
I am following this tutorial for expanding and collapsing my table view section. As this demo is done in swift 2.2 I have made all the changes according to swift 3.0 . I am stuck at the below function at if condition(currentSectionCells[row]["isVisible"]) which gives me error as "Type 'NSFastEnumerationIterator.Element' (aka 'Any' has no subscript members)'".
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
I have tried type casting it as below
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((((currentSectionCells) as? NSMutableArray)?.count)! - 1) {
let temp = [currentSectionCells][row] as? NSMutableDictionary
let temp2 = temp?["isVisible"] as! Bool
if temp2 == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
But this gives me a crash at runtime on this line "let temp2 = temp?["isVisible"] as! Bool"
Crash says "EXC_BAD_INSTRUCTION" and the temp shows as nil.
Please help guys. TIA
Table View Delegate and Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if cellDescriptors != nil {
return cellDescriptors.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return visibleRowsPerSection[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "sectionCellIdentifier" {
if let primaryTitle = currentCellDescriptor["secondaryTitle"]
{
cell.sectionTitleLabel.text = primaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "shortAnswerCell" {
cell.questionTitle.text = currentCellDescriptor["primaryTitle"] as? String
cell.questionTextView.text = currentCellDescriptor["secondaryTitle"] as? String
cell.answerTextView.text = currentCellDescriptor["answerTitle"] as? String
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let temp = cellDescriptors[indexPath.section] as? NSArray
let temp2 = temp?[indexOfTappedRow ] as? NSDictionary
let temp3 = temp2?["isExpandable"] as! Bool
if temp3 == true {
var shouldExpandAndShowSubRows = false
if temp3 == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
temp2?.setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (temp2?["additionalRows"] as! Int)) {
(temp![i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
tblExpandable.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
I worked on that tutorial as well and completed it successfully in swift3.Your solution is given below modify accordingly.
class yourClass: UIViewController
{
#IBOutlet weak var profileTableView: UITableView!
internal var visibleRowsPerSection = [[Int]]()
internal var cellDescriptors: NSMutableArray!
// VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
profileTableView.showsVerticalScrollIndicator = false
loadProfileControllerData()
profileTableSetUp()
// Do any additional setup after loading the view.
}
func loadProfileControllerData(){
if let path = Bundle.main.path(forResource: "CellDescriptor", ofType: "plist") {
cellDescriptors = NSMutableArray(contentsOfFile: path)
}
getIndicesOfVisibleRows()
profileTableView.reloadData()
}
// SHOW PARENT VISIBLE ROWS AND SAVE THERE ROW INDEX IN ARRAY
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors.objectEnumerator().allObjects as! [[[String:Any]]]{
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
print(visibleRowsPerSection)
}
}
// GET REQUIRED OBJECT OF TYPE [String: Any]
func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
let data = cellDescriptorss.object(at: indexOfVisibleRow) as! [String:Any]
return data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//----------------------
// EXTENSION TO OUR PROFILE CLASS THAT DETERMINE OUR CLASS CONFIRM 2 IMPORTANT DELEGATES
extension profileViewController : UITableViewDelegate,UITableViewDataSource{
//MARK-: TABLE VIEW DELEGATE FUNCTIONS
// RETURN NUMBER OF SECTION IN TABLE VIEW
public func numberOfSections(in tableView: UITableView) -> Int{
if cellDescriptors.count != 0{
return cellDescriptors.count
}
else{
return 0
}
}
// RETURN NUMBER OF ROWS IN EACH SECTION OF TABLE VIEWS
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return visibleRowsPerSection[section].count
}
/* Return object of UITableViewCell that contains table SECTON data and USER profile data */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let menuCell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! yourCellClass
if currentCellDescriptor["cellIdentifier"] as! String == "parent"{
}
else if currentCellDescriptor["cellIdentifier"] as! String == "child"{
menuCell.backgroundColor = UIColor.clear
}
return menuCell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
var data = cellDescriptorss.object(at: indexOfTappedRow) as! [String:Any]
if data["isExpandable"] as! Bool == true{
var shouldExpandAndShowSubRows = false
if data["isExpanded"] as! Bool == true{
shouldExpandAndShowSubRows = false
(cellDescriptorss[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
}
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (data["additionalRows"] as! Int)) {
(cellDescriptorss[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
self.profileTableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
Thank You for helping me out, I was stuck at a point where the sections weren't expanding even after your help, so just made some changes in the syntax as Swift 3.0 is very specific about type casting hence the didSelectRowAt wasn't functioning properly. Here is the complete didSelectRowAt method. Happy coding.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["isExpanded"] as! Bool == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
((cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + ((cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["additionalRows"] as! Int)) {
((cellDescriptors[indexPath.section] as! NSMutableArray)[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
Swift 3/4 without use of NSMutable arrays based on the tutorial and all the code wrapped in a model.
class CellsDescriptorModel {
private var cellDescriptors: [[[String:Any]]]!
private var visibleRowsPerSection : [[Int]]
var CellDescriptors : [[[String:Any]]] { get { return cellDescriptors }}
var VisibleRowsPerSection : [[Int]] { get { return visibleRowsPerSection }}
init(plist:String) {
visibleRowsPerSection = [[Int]]()
if let url = Bundle.main.url(forResource:plist, withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
cellDescriptors = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [[[String:Any]]]
} catch {
print(error)
}
}
getIndicesOfVisibleRows()
}
func getCellDescriptorForIndexPath(indexPath: IndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
return cellDescriptors[indexPath.section][indexOfVisibleRow]
}
func expandCell(indexPath:IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false {
shouldExpandAndShowSubRows = true
}
cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] = shouldExpandAndShowSubRows
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = shouldExpandAndShowSubRows
}
}
else {
if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "DataPickerTableViewCell" {
var indexOfParentCell: Int!
for index in (0..<indexOfTappedRow).reversed() {
if cellDescriptors[indexPath.section][index]["isExpandable"] as! Bool == true {
indexOfParentCell = index
break
}
}
cellDescriptors[indexPath.section][indexOfParentCell]["secondaryTitle"] = ""
cellDescriptors[indexPath.section][indexOfParentCell]["isExpanded"] = false
for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = false
}
}
}
getIndicesOfVisibleRows()
}
private func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
}
I have a tableView currently that is being populated with content from a firebase database. I am able to print out the specific information that is to be populated in the cells, and the cells physically are being populated indicated by the ability to scroll the appropriate length of the populated data. I am not sure as to why it would be accessing the information and populating the cells, but not inserting the data into the cells?
TableView:
import UIKit
import FirebaseAuth
import FirebaseStorage
class Articles: UITableViewController {
var vcType:String = "Home"
//var valueTopass = [[String:String]]()
var rooms = [Room]()
var articleCell = ArticlesCell()
#IBOutlet weak var menuButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.vcType == "Home"
{
self.rooms += ArticlesManager.sharedClient.rooms
}
else
{
if let obj = ArticlesManager.sharedClient.catRooms[self.vcType.lowercased()] //as? [Room]
{
self.rooms += obj
}
}
self.tableView.reloadData()
ArticlesManager.sharedClient.blockValueChangeInRoomArray = {
newRoom in
if self.vcType == "Home"
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
}
else
{
if self.vcType.lowercased() == newRoom.category
{
self.rooms.append(newRoom)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
self.tableView.reloadData()
}
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).row == 0 {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "featured", for: indexPath) as! featuredCell
let room = rooms[(indexPath as NSIndexPath).row]
cell2.configureCell(room)
self.tableView.reloadData()
return cell2
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ArticlesCell
let room = rooms[(indexPath as NSIndexPath).row]
cell.configureCell(room)
self.tableView.reloadData()
return cell
}
}
}
ArticleManager:
import UIKit
class ArticlesManager: NSObject {
static let sharedClient = ArticlesManager()
var dateFormater:DateFormatter{
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss a z"
return df
}
var roomsitems = ["Home", "News", "Features", "Opinion", "Sports", "Entertainment", "Editor"]
var rooms = [Room]()
var catRooms = [String:[Room]]()
var blockValueChangeInRoomArray:((Room) -> ())!
func fetchData()
{
Data.dataService.fetchData {
(room) in
self.rooms.append(room)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
if let obj = self.catRooms[room.category] //as? [Room]
{
var objRooms = obj
objRooms.append(room)
objRooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending })
self.catRooms[room.category] = objRooms
}
else
{
self.catRooms[room.category] = [room]
}
if self.blockValueChangeInRoomArray != nil
{
self.blockValueChangeInRoomArray(room)
}
}
}
}
DataService:
import UIKit
class ArticlesManager: NSObject {
static let sharedClient = ArticlesManager()
var dateFormater:DateFormatter{
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss a z"
return df
}
var roomsitems = ["Home", "News", "Features", "Opinion", "Sports", "Entertainment", "Editor"]
var rooms = [Room]()
var catRooms = [String:[Room]]()
var blockValueChangeInRoomArray:((Room) -> ())!
func fetchData()
{
Data.dataService.fetchData {
(room) in
self.rooms.append(room)
self.rooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending
})
if let obj = self.catRooms[room.category] //as? [Room]
{
var objRooms = obj
objRooms.append(room)
objRooms.sort(by: {
if $0.created_Date == nil
{
return false
}
if $1.created_Date == nil
{
return true
}
return $0.created_Date.compare($1.created_Date) == ComparisonResult.orderedDescending })
self.catRooms[room.category] = objRooms
}
else
{
self.catRooms[room.category] = [room]
}
if self.blockValueChangeInRoomArray != nil
{
self.blockValueChangeInRoomArray(room)
}
}
}
}
Remove this line of code from cellForRowAt indexPath method:
self.tableView.reloadData()
This is reloading Table continuously without giving it chance to configureCell and display data.
I had been able to figure out the particular issue that I was having. As I am using firebase, the rules as to who could "read" was, as default, set to authenticated users. In order to change this, you must go into the firebase console and either change it to public or authenticate users before loading the table view. Either way, I hope this was able to help you!