I have a custom cell for my UITableView, I will show first characters of the item name like this"below image"
so my question is how to create it how to fetch first letter pick from name data fetch via json.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProjectTabTableViewCell
cell.colorViewOutlet.layer.cornerRadius = 25.0
let projectLst = projectList[indexPath.row]
let str = projectLst.NameEN
print(str?.characters.first?.description ?? "");
projectNameFirst = [str?.characters.first?.description ?? ""]
print(projectNameFirst)
cell.wordsLabel?.text = projectNameFirst[indexPath.row]
cell.nameLabel?.text = projectLst.NameEN
cell.colorViewOutlet.backgroundColor = .random
return cell
}
let stringInput = "Ramu"
let stringInputArr = stringInput.components(separatedBy: " ")
var stringNeed = ""
for string in stringInputArr {
stringNeed = stringNeed + String(string.characters.first!)
}
print(stringNeed)
To get first character from string use below code
let str = "\(projectLst.NameEN)"
var result = ""
if str.characters.count > 0{
result = String(str.characters.prefix(1)) // which will return your first charcter
} else {
result = "" // result will be empty if string is empty
}
cell.wordsLabel?.text = result
Hope this will help you
Related
I try to use three collectionviews in one Viewcontroller. I parse the data like the following method shows:
At the bottom i add the data depending on the position to the right list (this part works)
func getEventData(eventIDs: [String], plz: String, positiona: Int){
for eventId in eventIDs {
let ref = Database.database().reference().child("Events").child(plz).child(eventId)
ref.observe(.value, with: { snapshot in
let item = snapshot.value as? [String: AnyObject]
let eventName = item?["name"] as! String
let date = item?["date"] as! String
let lat = item?["lat"] as! String
let lng = item?["lng"] as! String
let infos = item?["additionalInfos"] as! String
let position = item?["position"] as! String
let ts = item?["ts"] as! Int
let createdBy = item?["createdBy"] as! String
let timestamp = NSDate().timeIntervalSince1970
if (ts > Int(timestamp)) {
let eo = EventObject(eventID: eventId, eventName:
eventName, info: infos, createdBy: createdBy, date: date, lat: lat, lng: lng, position: position, ts: ts)
if positiona == 0{
self.acceptedEvents.append(eo)
self.acceptedEventscv.reloadData()
}else if positiona == 1{
self.myEvents.append(eo)
self.myEventscv.reloadData()
}else if positiona == 2{
self.storedEvents.append(eo)
self.storedEventscv.reloadData()
}
}
})
}
}
In my NumbersofItemsInSection method i did the following which works as well:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.acceptedEventscv{
return acceptedEvents.count
}else if collectionView == self.storedEventscv{
return storedEvents.count
}else if collectionView == self.myEventscv{
return myEvents.count
}else{
return 0
}
}
and in my CellForRowAtItem method i tried the following
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.acceptedEventscv{
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier:
"acceptedEventsCell", for: indexPath) as!
acceptedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase
space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "savedEventsCell", for: indexPath) as! savedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}
}
The problem is if i have more items in the seccond CollectionView, i always get this error:
Thread 1: Fatal error: Index out of range
at this line of code:
let eo = acceptedEvents[indexPath.row]
cellForItemAt also must be like
if collectionView == self.acceptedEventscv{
let item = acceptedEvents[indexPath.row]
----
return cell
}else if collectionView == self.storedEventscv{
let item = storedEvents[indexPath.row]
----
return cell
}else {
let item = myEvents[indexPath.row]
----
return cell
}
what happens now in your case is that you access the same array acceptedEvents in both cases where the returned count in numberOfItemsInSection may be different
Screen gets hanged and terminated. I have trace down that because of my while loop ,screens hanged and app terminated.
While loop runs only of 10 times. when I reopen my application my Data is got saved.
How to null or deallocate the UITableViewcell
let itemcell:TableViewCell = self.tableView.cellForRow(at: item) as! TableViewCell
Since self.tableView.dequeueReusableCell(withIdentifier: "listdetails", for: item) returning empty value .I have to use cellforRow.
Please provide input on this.
#IBAction func saveButton(_ sender: UIBarButtonItem) {
let ndx = IndexPath(row:0, section: 0)
var counter:Int = 0
let cell = self.tableView.cellForRow(at: ndx) as! TableViewCell
let locationNameCell = self.tableView.cellForRow(at: IndexPath(row:1, section: 0)) as! TableViewCell
let shoppingDetails = ShoppingDetails(context:managedContext)
let storeName = cell.storeName.text!
let storeFlag = validateShoppingList(storeName: storeName)
if storeFlag == true {
let shoppingDetails = ShoppingDetails(context:managedContext)
var listDetails :ListDetails!
while counter < sectionRowCount {
let item = IndexPath(row:counter, section: 1)
var itemcell = self.tableView.cellForRow(at: item) as! TableViewCell
let list = shoppingDetails.shoppingToList?.mutableCopy() as! NSMutableSet
listDetails = ListDetails(context:managedContext)
let listItem = itemcell.listItem.text!.trimmingCharacters(in: .whitespaces)
listDetails.listItem = listItem
var qty = itemcell.qtytextfield.text!
if qty.isEmpty {
qty = "0"
}
var units = itemcell.unitstextfield.text!
if units.isEmpty {
units = ""
}
listDetails.qty = Int64(qty)!
listDetails.units = itemcell.unitstextfield.text!
listDetails.isChecked = false
list.add(listDetails)
shoppingDetails.addToShoppingToList(list)
counter = counter + 1
list.removeAllObjects()
}
shoppingDetails.storeName = storeName
if locationNameCell.locationName.text != nil {
shoppingDetails.location = locationNameCell.locationName.text!
}
shoppingDetails.initialLetter = (cell.storeName.text!).first?.description
let seqNo:Int = ShoppingDetails.getSeqNo(managedObjectContext: managedContext) + 1
shoppingDetails.seqNo = Int32(seqNo)
coreData.saveContext()
let vc = storyboard!.instantiateViewController(withIdentifier: "StoreDisplayController") as! StoreDisplayController
vc.managedContext = managedContext
vc.coreData = coreData
vc.storeName = cell.storeName.text!
// managedContext.delete(shoppingDetails)
// managedContext.delete(listDetails)
// self.present(vc, animated:true, completion:nil)
// self.navigationController?.popToViewController(vc, animated: true)
self.navigationController?.pushViewController(vc, animated: true)
}
enter image description here
Save your data in this method as the user will scroll to fill all content
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// ...
//fill model array parameters from cell properties before data lost
var itemcell = self.tableView.cellForRow(at: indexPath) as! TableViewCell
let list = shoppingDetails.shoppingToList?.mutableCopy() as! NSMutableSet
listDetails = ListDetails(context:managedContext)
let listItem = itemcell.listItem.text!.trimmingCharacters(in: .whitespaces)
listDetails.listItem = listItem
var qty = itemcell.qtytextfield.text!
if qty.isEmpty {
qty = "0"
}
var units = itemcell.unitstextfield.text!
if units.isEmpty {
units = ""
}
listDetails.qty = Int64(qty)!
listDetails.units = itemcell.unitstextfield.text!
listDetails.isChecked = false
list.add(listDetails)
shoppingDetails.addToShoppingToList(list)
}
then when save button is clicked check that model array for empty item and scroll to that indexpath to fill
I have some trouble reusing cells in swift. I want the code below to only execute for the cells where post.altC.isEmpty actually is true. The problem is that it makes botBtnsStackView.isHidden = true for all cells, even though altC is not empty in all. What am I doing wrong?
The code below is from my PostCell file(just a part of the configureCell code at the bottom, but it's this part that is going wrong):
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as? PostCell{
cell.configureCell(post: post)
return cell
} else {
return PostCell()
}
}
ConfigureCell from PostCell file:
func configureCell(post: Post) {
self.post = post
//ALT A
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt A")
}
//ALT B
if post.altB.isEmpty == false {
altBLabel.text = post.altB["text"] as? String
if let votes = post.altB["votes"]{
self.altBVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt B")
}
//ALTD
if post.altD.isEmpty == false {
altDLabel.text = post.altD["text"] as? String
if let votes = post.altD["votes"]{
self.altDVotesLbl.text = "\(votes)"
}
} else {
altDView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
altDVotesView.isHidden = true
altDLabelView.isHidden = true
}
//ALT C
if post.altC.isEmpty == true {
print("No data found in Alt C")
//altCView.isHidden = true
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Cells are reused. So anything you do in an if statement you need to undo in the else.
So your snipped needs to be changed to:
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
botBtnsStackView.isHidden = false
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Your others need to be update as well. For example, for "ALT A":
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
altALabel.text = ""
self.altAVotesLbl.text = ""
print("No data found in Alt A")
}
I'm guessing a bit here but this gives you an idea. Adjust this to suit your actual needs. The important thing to remember is that whatever you set for one condition, you must reset for other conditions.
Unrelated but you should rewrite your cellForRowAt as:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as! PostCell
cell.configureCell(post: post)
return cell
}
This is a case where a force-cast is appropriate. You want your app to crash early on in development if you have setup your cell identifier and cell type incorrectly. Once setup properly and working, it can't crash at runtime unless you do something to break it.
I'm attempting to load a table view with two different prototype cells. profileCell should only load once and at the top of the table view. dogCell should count an array of dog objects named dogs downloaded from firebase. Currently, only the first cell is displaying correctly.
I think the numberOfRowsInSection method isn't accurately counting the dog objects in the dogs array. When I put a breakpoint on return dogs.count + 1 and po dogs.count the debugger keeps outputting 0.
When I use return dogs.count the table view loads but with only the profile cell. If I use return dogs.count + 1(to account for the profile cell at the top) an exception is thrown when constructing dogCell: "fatal error: Index out of range"
Perhaps I need to change the way my tableview is reloading data?
Here's my code:
class DogTableViewController: UITableViewController {
var user = User()
let profileCell = ProfileTableViewCell()
var dogs = [Dog]()
override func viewDidLoad() {
super.viewDidLoad()
let userDogRef = Database.database().reference().child("users").child(user.uid!).child("dogs")
let userProfileImageView = UIImageView()
userProfileImageView.translatesAutoresizingMaskIntoConstraints = false
userProfileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.layer.cornerRadius = 20
userProfileImageView.clipsToBounds = true
userProfileImageView.contentMode = .scaleAspectFill
userProfileImageView.image = UIImage(named: "AppIcon")
navigationItem.titleView = userProfileImageView
//MARK: Download dogs from firebase
userDogRef.observe(.childAdded, with: { (snapshot) in
if snapshot.value == nil {
print("no new dog found")
} else {
print("new dog found")
let snapshotValue = snapshot.value as! Dictionary<String, String>
let dogID = snapshotValue["dogID"]!
let dogRef = Database.database().reference().child("dogs").child(dogID)
dogRef.observeSingleEvent(of: .value, with: { (snap) in
print("Found dog data!")
let value = snap.value as? NSDictionary
let newDog = Dog()
newDog.name = value?["name"] as? String ?? ""
newDog.breed = value?["breed"] as? String ?? ""
newDog.creator = value?["creator"] as? String ?? ""
newDog.score = Int(value?["score"] as? String ?? "")
newDog.imageURL = value?["imageURL"] as? String ?? ""
newDog.dogID = snapshot.key
URLSession.shared.dataTask(with: URL(string: newDog.imageURL!)!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
newDog.picture = UIImage(data: data!)!
self.dogs.append(newDog)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}).resume()
})
}
})
tableView.estimatedRowHeight = 454
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dogs.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row].breed
dogCell.dogNameLabel.text = dogs[indexPath.row].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row].score)
dogCell.dogImageView.image = dogs[indexPath.row].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}
}
I actually found a solution shortly after writing this question, but I think it might be helpful for others to read.
Because the first indexPath.row is dedicated to a profile cell, I should not have been using the indexPath.row to navigate my dogs array. Instead I should have been using indexPath.row - 1 to get the correct dogs index.
Here's the section I updated:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row - 1].breed
dogCell.dogNameLabel.text = dogs[indexPath.row - 1].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row - 1].score)
dogCell.dogImageView.image = dogs[indexPath.row - 1].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row - 1].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}
When you scroll through the table appear lags. Pictures, or long texts in the database do not. Sorry, for my bad English
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("audioCell", forIndexPath: indexPath) as? AudiosTableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "audioCell") as? AudiosTableViewCell
} else {
let realm = try! Realm()
let audios = realm.objects(Music)[indexPath.row]
let duration = audios.duration
var durationString = ""
if duration/60 < 10 {
durationString = durationString + "0" }
durationString = durationString + String(duration/60) + ":"
if duration%60 < 10 {
durationString = durationString + "0" }
durationString = durationString + String(duration%60)
cell!.artistLabel.text = audios.artist
cell!.titleLabel.text = audios.title
cell!.durationLabel.text = durationString
}
return cell!
}
If you need additional information, write exactly what you need. I've reviewed a lot of information, tried many methods, the third day I am suffering, it does not work
This
let realm = try! Realm()
should be done on videoDidLoad or similar but only once I suggest
if audios.count == 0 {
let realm = try! Realm()
let audios = realm.objects(Music)[indexPath.row]
}
then replace
let realm = try! Realm()
let audiosStore = realm.objects(Music)
with
let audios = audiosStore[indexPath.row]
When you call
let realm = try! Realm()
You are asking for all the objects from Realm each time.
Can you move this code to out of cellForRow, and be sure call it before cellForRow triggers.
let realm = try! Realm()
tableView.dequeueReusableCellWithIdentifier("audioCell", forIndexPath: indexPath) never returns nil. If you already know cell will have class AudiosTableViewCell you can rewrite code as follows:
// Make realm property of your view controller
let realm: Realm!
// In viewDidLoad
realm = try! Realm()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("audioCell", forIndexPath: indexPath) as! AudiosTableViewCell
let audios = realm.objects(Music)[indexPath.row]
let duration = audios.duration
var durationString = ""
if duration/60 < 10 {
durationString = durationString + "0" }
durationString = durationString + String(duration/60) + ":"
if duration%60 < 10 {
durationString = durationString + "0" }
durationString = durationString + String(duration%60)
cell.artistLabel.text = audios.artist
cell.titleLabel.text = audios.title
cell.durationLabel.text = durationString
return cell
}