Avoiding Empty Entry - iphone

Guys how to avoid adding an empty items to a toDoList table ? I've been busy with building a toDo list and came to a problem when a user doesn't fill anything in the textfield but he can still add the empty item to the table, and when I try to swipe the table's row with an empty entry I get a fatal error ! why is that and how to avoid adding those empty data ?
OK, I added your code to my add Button : But it didn't help that much!
#IBAction func addItem(sender: AnyObject) {
if "" != item.text {
// DO Something but don't fill the row (**item** is my text field)
toDoList.append(item.text)
item.text = ""
NSUserDefaults.standardUserDefaults().setObject(toDoList, forKey: "toDoList")
}else{
labelField.text = "Please enter an item!"
}
}

In the method that you add your item just check the text property of your UITextField instance and if it contains string then add the item
if "" != textField.text {
}
Though using the solution I gave has some drawbacks such as user can just enter space as its input but it does the job. You can also use regular expression too in order to restrict the input you want user enters.

you can control the input of an item like this
if "" == textField.text {
// DO Something but don't fill the row
}
and for , the swipe problem, you can implement the following Method
optional func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if commitEditingStyle == .Delete {
// Delete the row, and the item in the array
}
}

OK, Fixed it! That's how I did it :
if item.text != "" {
// DO Something but don't fill the row
toDoList.append(item.text)
}else{
labelField.text = "Please enter an item!"
}

Related

how to remove the cell from uitableview cell

Im trying to dynamically arranging table view when user select "type 3". It works when user select "type 3", "type 3-1" would be added in the tableview. However the program crashed when user select other than type3-1. I dont know how can I execute the "rows.remove(at:2)" before the override function is called. Any suggestion would appreciate!
class GuestViewController: UITableViewController {
var rows:[[[String:Any]]] = [[["type":RowType.DetailContent,
"subType":DCType.DCRightContent,
"name":CPFFields.CID,
"content":"9637"],
["type":RowType.DetailContent,
"subType":DCType.DCInput,
"name":CPFFields.VISIA]],
[["type":RowType.DetailTextView,
"CPFType":CPFFields.UV,
"title":CPFFields.preferenceTitle]],
[["type":RowType.DetailContent,
"subType":DCType.DCSelection,
"name":CPFFields.Phototherapy,
"title":CPFFields.anestheticTitle],
["type":RowType.DetailTextView,
"CPFType":CPFFields.Phototherapy,
"title":CPFFields.preferenceTitle]],
]
var isNewGuestSelected : Bool = false
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = rows[indexPath.section][indexPath.row]
let type = item["type"] as! RowType
if type == RowType.DetailContent
{
let cell = tableView.dequeueReusableCell(withIdentifier: "DetailNameCell", for: indexPath) as! DetailContentCell
let cpfType = item["name"] as? CPFFields ?? .Customer
cell.name.text = CPFFields.localizedString(from: cpfType)
if let field = item["title"] as? CPFFields
{
cell.name.text = CPFFields.localizedString(from: field)
}
cell.moreSlectionLeftSpace = true
var content:String? = ""
cell.type = cpfType
switch cpfType {
case .CID:
content = (profile?.birthDate.dateFromDateString?.stringForPaitentId ?? "") + (profile?.name ?? "")
case .CT:
content = ""
if let profile = profile
{
content = CPFCustomerType.localizedString(from: profile.type)
//New Guest
if(content == CPFCustomerType.type1.rawValue){
rows[0].insert(["type":RowType.DetailContent,
"subType":DCType.DCRightContent,
"name":CPFFields.CID,
"content":"9637"], at: 1)
isNewGuestSelected = true
} else{
if isNewGuestSelected == true{
rows[0].remove(at: 1)
isNewGuestSelected = false
}
}
}
let subType = item["subType"] as! DCType
cell.setcontentType(type: subType, content: content)
return cell
}
I expected not to see "rows[0][2]" after running "rows[0].remove(at:1)".
However the log is printing
rows[0][0]
rows[0][1]
rows[0][2]
then
it crashed at "let item = rows[indexPath.section][indexPath.row]"
because it is out of range
You are modifying your content while rendering, thus after numberOfRows:inSection: was called. Therefore the tableView is trying to access an element that no longer exists, since you removed it.
Your cycle:
→ number of rows 4
→ removed item, contents now has 3 items
→ cell for item 0
→ cell for item 1
→ cell for item 2
- cell for item 3 → crash
Consider replacing the logic you have here outside of the cellForRow method, and doing these operations before you reload your tableView.
You should use the tableView:cellForRow:atIndexPath strictly for dequeueing your cells and configuring them; not for modifying the underlying data store since funky things like you're experiencing now can happen.
If you provide a bit more context I can probably tell you where to place your code to fix this issue.
Actually, the solution is quite simple. I just added tableView.reloadData() after removing the array, and the UI can then be updated.
if isNewGuestSelected == true{
rows[0].remove(at: 1)
isNewGuestSelected = false
tableView.reloadData()
}

How to detect dismissed TextView in TableView?

I have a tableView with dynamic cells with multiple TextViews. I am dismissing a keyboard with a "Cancel" and trying to determine which TextView is being dismissed to "undo" the changes made by user.
Based on this similar question: How to determine which textfield is active swift I have adapted one of the answers for the following extension:
extension UIView {
var textViewsInView: [UITextView] {
return subviews
.filter ({ !($0 is UITextView) })
.reduce (( subviews.compactMap { $0 as? UITextView }), { summ, current in
return summ + current.textViewsInView
})
}
var selectedTextView: UITextView? {
return textViewsInView.filter { $0.isFirstResponder }.first
}
}
This is working and I am presently testing in the following code:
#objc func cancelButtonAction() {
if let test = tableView.selectedTextView {
print("View Found")
}
tableView.beginUpdates()
tableView.endUpdates()
}
Doing a break at print("View Found") I can inspect "test". The following is the result.
This appears to only identify the view Test by a memory address. My question is how do I interpret this to identify the view that was being edited?
Update: There seems to be some issue in understanding. Assume I have a table with two cells and in each cell two textViews (dynamic cells). Assume the table loads by saying in the 4 textViews. "Hi John", "Hi Sam", "Bye John", "Bye Sam". Suppose the user starts editing a cell and changes one cell to read "Nachos". Then the user decides to cancel. I want to then replace with the value that was there before (from my model). I can find the textView but it now reads "Nachos". Therefore I do not know which textView to reload with the appropriate Hi and Bye.
Implement a placeholder for your textviews so that when their text is empty, it will have a default value. Therefore, when a user presses cancel while in focus of a textview, we can set the textview's text to its default value. See link to implement a textview placeholder.. Text View Placeholder Swift
I did solve this problem by adding the .tag property to the textView objects. I also dropped the extension approach and used textView delegate. The solution required me to first assign the tag and delegate = self for each textView in the tableView: cellForRowAt. The following shows one TextView of many. Notice the tag is setup so I may determine the section and the row it came from and the specific item.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
...
cell.directionsTextView.delegate = self
cell.directionsTextView.tag = indexPath.section*1000 + indexPath.row+1
return cell
}
Two global variables are defined in my tableView class:
var activeTextView = UITextView()
var activeTextViewPresentText = String()
The textViewDidBeginEditing captures the original state of the textView text before the user starts editing.
// Assign the newly active textview to store original value and original text
func textViewDidBeginEditing(_ textView: UITextView) {
print("textView.tag: \(textView.tag)")
self.activeTextView = textView
self.activeTextViewPresentText = textView.text
}
Lastly, if the user cancels the editing, the original text is reloaded.
#objc func cancelButtonAction() {
if activeTextView.text != nil {
activeTextView.text = activeTextViewPresentText
}
self.view.endEditing(true)
tableUpdate()
}

how do I just show the profile Image next to the last message of an user in a chat

I am using JSQ Messages Controller to add a chat feature to my app, but I also managed to create one on my own with a collectionview. However Injust can‘t figure out how to show the profile picture just next to the last message of an user.
For example he writes 3 messages and no one else in the chat does write anything in between. Now I just want to show only next to the third message the profile picture. When I return the items (cells) I just can edit the item I am about to return (at indexPath.item) though. I can make sure that the message before (at indexPath.item - 1) has the same senderID. But I can‘t check if the cell at indexPath.item + 1) is from the same sender. Since I am not able to check the second one, I have no clue how to solve my problem. I hope you understood what I want to do.
First I would like to point you to the new project that is taking over JSQMessageViewController since JSQMessageViewController is deprecated, It is called MessageKit.
Make a function to determine if it is the last message in a set.
//Check if it is the last message in all your messages
//Check that the next message is not sent by the same person.
func isLastInSet(indexOfMessage: IndexPath) -> Bool {
if indexOfMessage.item == messages.count -1 {
return true
} else if {
return messages[indexOfMessage.item].senderID() != messages[indexOfMessage.item + 1].senderID()
}
}
Now just add a call to isLastInSet within the avatarImageDataForItemAt if it returns a false set the image to nil
func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
if let message = messages[indexPath.item]{
if isLastInSet(indexOfMessage: indexPath) {
return ImageData
}
}
return nil
}

How to filter table view cells with a UISegmentedControl in Swift?

I've already searched this before asking the question but I didn't find what I need.
I'm building this app where the user puts a task (not going to the app store, just for me and some friends), and the task has a category. For example: school, home, friends, etc. When the user is going to add a new task, there are 2 text fields, the description text field and the category text field. I'm using a UIPickerView so the user picks a category, then, after creating the new task, it will add the category to an array I've created called "categories".
I want to put an UISegmentedControl on top of the table view with the sections:
All - School - Home - Friends
If all is selected, it will show all the cells with no filtering. If not, it will show the cell(s) with the corresponding categories.
I've read that I need to create table view sections to each category, but this would change my code a lot, and I don't even have an idea of how to work with multiple table view sections, I've tried once but it kept repeating the cells of one section in the second.
So how can I filter the cells per category?
Can I just put for example this? :
if //code to check in which section the picker is here {
if let schoolCell = cell.categories[indexPath.row] == "School" {
schoolCell.hidden = true
}
}
Please help me!!!
EDIT:
I have this code by now:
if filterSegmentedControl.selectedSegmentIndex == 1 {
if categories[indexPath.row] == "School" {
}
}
I just don't know where to go from here. How do I recognize and hide the cells?
It seems to me that you may want to take a simpler approach first and get something working. Set up your ViewController and add a tableView and two(2) arrays for your table data. One would be for home and the other for work. Yes, I know this is simple but if you get it working, then you can build on it.
Add a variable to track which data you are displaying.
#IBOutlet var segmentedControl: UISegmentedControl!
#IBOutlet var tableView: UITableView!
// You would set this to 0, 1 or 2 for home, work and all.
var dataFilter = 0
// Data for work tasks
var tableDataWork : [String] = ["Proposal", "Send mail", "Fix printer", "Send payroll", "Pay rent"]
// Data for home tasks
var tableDataHome : [String] = ["Car payment", "Mow lawn", "Carpet clean"]
Add these functions for the segmented control.
#IBAction func segmentedControlAction(sender: AnyObject) {
switch segmentedControl.selectedSegmentIndex {
case 0:
print("Home")
dataFilter = 0
case 1:
print("Work")
dataFilter = 1
case 2:
print("All")
dataFilter = 2
default:
print("All")
dataFilter = 2
}
reload()
}
func reload() {
dispatch_async( dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("task-cell")
var title: String?
switch dataFilter {
case 0:
title = tableDataHome[indexPath.row]
case 1:
title = tableDataWork[indexPath.row]
case 2:
if indexPath.row < tableDataWork.count {
title = tableDataWork[indexPath.row]
} else {
title = tableDataHome[indexPath.row - tableDataWork.count]
}
default:
if indexPath.row < tableDataWork.count {
title = tableDataWork[indexPath.row]
} else {
title = tableDataHome[indexPath.row + tableDataWork.count]
}
}
cell?.textLabel?.text = title
if cell != nil {
return cell!
}
return UITableViewCell()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If
switch dataFilter {
case 0: return tableDataHome.count
case 1: return tableDataWork.count
default: return tableDataHome.count + tableDataWork.count
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
You can find the entire project here: https://github.com/ryantxr/segmented-control-app
It depends on your tableview.
If you use NSFetchedResultsController then you need to modify your fetch request. If you use an array directly, just use the filter function in Swift, passing in the condition, e.g. filteredArray = array.filter{$0.isAudioFile} Then, after setting your datasource array to the filtered one, call reloadData on your tableview.
You will need to keep a reference to the full array, and use the filtered one as your datasource in cellForRow...

Storing Individual UISwitch States in UITableView

I have a UITableView that has Two ProtoType Cells both with separate TableViewCell subclassess. In one of the prototype cells I have multiple switches. The user can select the item they want in the table and turn the switch on that corresponds to the item. I would like to be able to store the UISwitchstate so if the user navigates away and comes back they will see what they have selected previously.
I'm trying to store the UISwitchstate in a dictionary and then call the state back when the table gets reloaded.
Here is the code I have so far:
#IBAction func switchState(sender: AnyObject) {
if mySwitch.on{
savedItems = NSMutableDictionary(object: mySwitch.on, forKey: "switchKey")
standardDefaults.setObject("On", forKey: "switchKey")
}
else{
standardDefaults.setObject("Off", forKey: "switchKey")
and then this is in the awakeNib section:
override func awakeFromNib() {
super.awakeFromNib()
self.mySwitch.on = NSUserDefaults.standardUserDefaults().boolForKey("switchKey")
NSUserDefaults.standardUserDefaults().registerDefaults(["switchKey" : true])
}
thanks for the help.
What you need to do is:
Having a dictionary (or any other data type) that is from the exact same size as your data source.
And in your
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
you need to that dictionary for the row indexPath.row and then mainpulate your cell
Note, your dictionary should be static
Putting my answer here instead of a comment so I can hopefully explain things better. You need to have some sort of system to keep up with which dictionary item corresponds with which UISwitch. Your best bet would probably be to have a dictionary var uiDictionary = [String : Bool]() where your keys are a string that you know corresponds to a specific switch. Then, in your cellForRowAtIndexPath: you would try to access each dictionary item, check if it's the one for the switch you're trying to set, and then set it. Don't know your exact setup, but it would look something like this...
func cellForRowAtIndexPath() {
//other stuff here
//now set your switches
for (key, value) in uiDictionary {
switch(key) {
case "Switch1":
if value == true {
cell?.switch1.setOn(true, animated: true)
} else {
cell?.switch1.setOn(false, animated: true)
}
break
case "Switch2":
if value == true {
cell?.switch1.setOn(true, animated: true)
} else {
cell?.switch1.setOn(false, animated: true)
}
}
}
}