I want to save some values into an array from multiple textfields in a tableview cell. I realized my current implementation only will work if data is entered in a very specific way. This is what I've tried:
func textFieldDidEndEditing(_ textField: UITextField) {
if(textField.tag == 1){
self.weight = textField.text!
} else if(textField.tag == 2){
self.reps = textField.text!
}
if(self.reps != "" && self.weight != ""){
let set = ExerciseSet(weight: self.weight, reps: self.reps, RPE: "")
self.setsArray[setsArray.count - 1] = set
self.weight = ""
self.reps = ""
}
}
But this implementation would only work if data is entered, then the next cell is added, then entered. How can I save all the data into the array by accessing each textfield in each tableview cell?
You can create a function to loop table,get data and append ExerciseSet,
func getTableData(){
for i in 0..<tbl.numberOfRows(inSection: 0) { //tbl--> your table name
let cell = tbl.cellForRow(at: IndexPath(row: i, section: 0)) as! TableViewCell. //TableViewCell--> your tableview custom cell name
let set = ExerciseSet(weight: cell.txtWeight.text ?? "", reps: cell.txtReps.text ?? "", RPE: "") //txtWeight,txtReps your 2 text field names
self.setsArray[setsArray.count - 1] = set
}
}
Related
I’m using the below code to get the cursor position when the user types something in the textView.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
inputChar = text
inputRange = range
inputIndex = inputRange?.upperBound
self.presenter.hashtagDataArray.removeAll()
if(text == "\n") {
textView.resignFirstResponder()
toggleTableView(toggle: true)
return false
}
let str = (textView.text + text)
if str.utf16.count <= MediaPostViewController.descCharacterLimits {
return true
}
let numberOfChars = str.utf16.count
lbbbl_DescCount.text = "\(textView.text.utf16.count)/\(MediaPostViewController.descCharacterLimits)"
return (numberOfChars <= MediaPostViewController.descCharacterLimits) || (str.utf16.count < textView.text.utf16.count)
}
func textViewDidChange(_ textView: UITextView) {
var indexPosition : Int?
updateCharacterCount()
self.mainScrollView.isScrollEnabled = false
if textView.text != "" {
inputIndex = (String(textView.text.utf16) as NSString?)?.substring(with: NSRange(location: 0, length: inputRange!.location)).count
cursorPosition = inputIndex! + 1
inputText = textView.text!
textViewEndIndex = textView.text.unicodeScalars.endIndex.utf16Offset(in: textView.text)
if #available(iOS 10.2, *) {
inputText = textView.text.replaceEmoji(with: "#")
} else {
//Fallback on earlier versions
}
if inputChar == "" || inputChar == " "{
indexPosition = cursorPosition!-2
}
else {
indexPosition = cursorPosition!-1
}
guard let enteredText = inputText?.utf16.subString(from: 0, to: indexPosition!) else { return }
guard let lastdelimiterposition = enteredText.lastIndexPosition(of: "#") else { return }
hashwordstartIndex = lastdelimiterposition
checkhashinword = inputText?.utf16.subString(from: lastdelimiterposition, to: indexPosition!)
if inputChar == "" || inputChar == " "{
spaceCharactersCheck = 1
}
if spaceCharactersCheck == 1{
checkhashword = checkhashinword?.components(separatedBy: " ").filter({!$0.contains("#")}).joined(separator: " ")
checkhashedword = checkhashinword?.components(separatedBy: " ").filter({$0.contains("#")}).joined(separator: " ")
}
if let checkhashinword = checkhashinword {
if checkhashinword.utf16.count > 1 && !(checkhashinword.contains(" ")){
self.presenter.returnHashTagsData((checkhashinword.utf16.subString(from: 1, to: checkhashinword.utf16.count-1))!)
}
}
}
else {
self.toggleTableView(toggle: true)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let rowSelected = tableView.cellForRow(at: indexPath)?.textLabel?.text
var textViewValue : String?
textViewValue = textView_Desc.text!
let startIndex = String.Index(utf16Offset: hashwordstartIndex!, in: String(textViewValue!))
let hashwordendIndex = String.Index(utf16Offset: inputIndex!, in: String(textViewValue!))
let range = startIndex...hashwordendIndex
if var strNewText = textViewValue?.components(separatedBy: "#") {
if strNewText.count > 1 {
if let textlabel = rowSelected {
strNewText[strNewText.count - 1] = textlabel
}
}
if var rowSelected = rowSelected {
if let checkhashword = checkhashword
{
if checkhashword != ""
{
rowSelected = rowSelected.appending(" ").appending(checkhashword).appending(String(checkhashedword!))
}
}
if textViewValue != ""{
if textViewEndIndex != inputIndex {
do {
if let result = textViewValue?.replaceSubrange(range, with: rowSelected)
{
print("result:\(result)")
}
else {
throw RangeException.notaValidRange
}
}
catch {
}
}
}
spaceCharactersCheck = 0
checkhashword = ""
checkhashedword = ""
}
let combinedText = strNewText.joined(separator: "#")//.appending(" ")
if combinedText.count-1 > MediaPostViewController.descCharacterLimits {
if textView_Desc.text.count-1 > MediaPostViewController.descCharacterLimits {
toggleTableView(toggle: true)
return
}
}
if textViewEndIndex != inputIndex {
textView_Desc.text = textViewValue
}
else {
textView_Desc.text = combinedText.replacingOccurrences(of: "##", with: "#")
}
lbbbl_DescCount.text = "\(textView_Desc.text.count)/\(MediaPostViewController.descCharacterLimits)"
}
toggleTableView(toggle: true)
self.hideScrollView.isHidden = false
self.view.bringSubviewToFront(hideScrollView)
}
}
Use case :
Trying to implement hashtags similar to Instagram.
Approach:
There is a textView and I’ve added a tableView beneath that. The tableView gets data from API call based on the user input in textView. For instance, if user types #a then I show the tableView and tableView is loaded with suggestions like (#abc,#abcd, etc) from API call. The user can select a row and after selection, I hide the tableView. It works perfectly fine when the user enters hashtags between a text like #abc #insta and say if the user tries to type #ba between this #abc #insta it gets perfectly inserted after user chooses a suggestion from tableView (like #abc#bat#insta)
Problem:
When I have emojis the text replaces the emoji. For eg: if the user enters emoi#a then it will get a list starting with #a (like #abc,#ab etc) and now if the user selects #abc then it populates the textView like #abc#a and emoji disappears.
I don’t see a solution to this problem in any Github repositories.
Has anyone faced a similar problem?
I have code to sum four text fields and output the total in a label. Currently the code sums the fields after finishing editing, that is, selecting another text field. Is there a way to sum the text fields as the user types?
#IBAction func TankFuelChanged(_ sender: Any) {
let leftMainTankQuantityValue = Int(leftMainTankQuantity.text ?? "") ?? 0
let rightMainTankQuantityValue = Int(rightMainTankQuantity.text ?? "") ?? 0
let auxTankQuantityValue = Int(auxTankQuantity.text ?? "") ?? 0
let tailTankQuantityValue = Int(tailTankQuantity.text ?? "") ?? 0
let total = leftMainTankQuantityValue + rightMainTankQuantityValue + auxTankQuantityValue + tailTankQuantityValue
totalFuelLoad.text = "\(total)"
What you are looking for is an event triggered when text field changes. You can drag an action from storyboard or you can add them programmatically by using addTarget similar to UIButton but need to use event editingChanged. Check the following code:
var allTextFields: [UITextField] {
return [leftMainTankQuantity, rightMainTankQuantity, auxTankQuantity, tailTankQuantity]
}
override func viewDidLoad() {
super.viewDidLoad()
allTextFields.forEach { $0.addTarget(self, action: #selector(onTextFieldChange), for: .editingChanged) }
}
#objc private func onTextFieldChange() {
updateResult()
}
private func updateResult() {
let strings: [String] = allTextFields.compactMap { $0.text } // Will remove all nil texts
let integers: [Int] = strings.compactMap { Int($0) } // Will remove all non-integer texts
let sum = integers.reduce(0, { $0 + $1 }) // Will compute a sum
print(sum) // TODO: update your result here
}
A method must be marked #objc because of the #selector next to that I hope code speaks for itself.
I have a textView where user can add comments and mention to other users.
I've build a function which is triggered when the user type the sign "#".
So basically as in Instagram or Facebook when the user types "#" a tableview appears and show the user suggestions.
Here's my function:
func suggestUser() {
if let searchText = postTextField.text {
let words = searchText.components(separatedBy: .whitespacesAndNewlines)
for var word in words {
if word.hasPrefix("#") {
word = word.trimmingCharacters(in: .punctuationCharacters)
let userToSearch = String(word.dropFirst())
self.viewContainerForTableView.isHidden = false
self.suggestedUsers.removeAll()
self.tableView.reloadData()
Api.User.queryUsersByMentionName(WithText: userToSearch, completion: { (user) in
if !self.suggestedUsers.contains(where: { $0.id == user.id }) {
self.suggestedUsers.append(user)
}
self.tableView.reloadData()
})
} else {
self.viewContainerForTableView.isHidden = true
}
}
}
}
I have two issue:
1) When the user clicks on the suggested user in the tableview, how can i remove the text he already typed and add the one he selected?
Let me give you an example:
If a user types #jan in the tableView appears janedoe. When the user click on the suggested name in table view how can i remove jan and add janedoe?
Here's my code for the didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let username = suggestedUsers[indexPath.row].username
let usernameToAppend = username.components(separatedBy: .whitespacesAndNewlines).joined()
postTextField.text.append("\(usernameToAppend)")
}
2) Is there a way to check if an user is already typed in the textView and so not displaying it in the tableview?
Thank you!
After an entire day of trying i Think I've found a solution... Hopefully...
so I have created an extension for my textView:
extension UITextView {
var currentWord : String? {
let beginning = beginningOfDocument
if let start = position(from: beginning, offset: selectedRange.location),
let end = position(from: start, offset: selectedRange.length) {
let textRange = tokenizer.rangeEnclosingPosition(end, with: .word, inDirection: 1)
if let textRange = textRange {
return text(in: textRange)
}
}
return nil
}
}
Then in my didSelectRowAt i have:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let username = suggestedUsers[indexPath.row].username
let usernameToAppend = username.components(separatedBy: .whitespacesAndNewlines).joined()
let currentWord = postTextField.currentWord
if currentWord != nil && currentWord != "#" {
if let countIndex = currentWord?.count {
let count = Int(countIndex)
let startPosition = postTextField.selectedTextRange?.start
let endPosition = postTextField.position(from: startPosition!, offset: -count)
postTextField.selectedTextRange = postTextField.textRange(from: startPosition!, to: endPosition!)
if let range = postTextField.selectedTextRange {
postTextField.replace(range, withText: usernameToAppend)
}
}
} else if currentWord == "#" {
if let range = postTextField.selectedTextRange {
if range.start == range.end {
postTextField.replace(range, withText: usernameToAppend)
}
}
}
}
}
How do I set up the buttons that are linked to didPressNumber to add to each other when pressed so lets say its a calculator and I want set it up where each button is pressed has a letter and number value when it is pressed it adds to the previous one press and I want to set up 2 labels one displaying the number value and one displaying the letter value and how would I set up the value of each number?
enum modes {
case not_set
case addition
case subtraction
case equals
}
#IBAction func didPressNumber(_ sender: UIButton) {
let stringValue:String? = sender.titleLabel?.text
if (lastButtonWasMode) {
lastButtonWasMode = false
labelString = "0"
}
labelString = labelString.appending(stringValue!)
updateText()
}
func updateText() {
guard let labelInt:Int = Int(labelString) else {
return
}
if (currentMode == .not_set) {
savedNum = labelInt
}
let formatter: NumberFormatter = NumberFormatter()
formatter.numberStyle = .decimal
let num:NSNumber = NSNumber(value: labelInt)
label.text = formatter.string(from: num)
}
func changeMode(newMode:modes) {
if (savedNum == 0) {
return
}
currentMode = newMode
lastButtonWasMode = true
}
I have this problem, I have a tableview with 3 different kind of news manage by segmented control. When I scrolled the news stays for example in the fifth new, if I click in the second segment, appears in the position 5 . Is not showing the news from the segment 1(the new one),at row 0 (beginning) , stays in the position I leave when I was scrolling. Why is happening this? what I'm doing wrong?. I'm using one tableview for the 3 different kinds of news and reload the tableview data every time I change the segment.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
NSLog("selectedSegmentIndex: \(self.sectionSegmentedControl.selectedSegmentIndex) - Row: \(indexPath.row)")
let cell:UITableViewCell = self.tableview.dequeueReusableCellWithIdentifier("Cell")!
// Grab the elements using the tag
let labelTitle:UILabel? = cell.viewWithTag(1) as! UILabel?
let labelSection:UILabel? = cell.viewWithTag(2) as! UILabel?
let labelDate:UILabel? = cell.viewWithTag(3) as! UILabel?
let imageView:UIImageView? = cell.viewWithTag(4) as! UIImageView?
// Check which segment to get data from
switch self.sectionSegmentedControl.selectedSegmentIndex {
case 0:
// If segment is 0, take data from cover News
if (indexPath.row <= self.coverNews.count - 1){
//Current new to display
let currentNewToDisplay = self.coverNews[indexPath.row]
//let currentNewToDisplay = self.news[indexPath.row]
// Get the image and assign to the imageView
if let actualImageView = imageView {
// Imageview actually exists
if currentNewToDisplay.imageUrl != "" {
// Image url exists, so download it
let imageUrl:NSURL? = NSURL(string: currentNewToDisplay.imageUrl)
// Download image with SDWebImage library
if let url = imageUrl {
actualImageView.sd_setImageWithURL(url)
}
}
}
// Get the news title and assign to the label
if let actualLabelTitle = labelTitle {
let title = currentNewToDisplay.title
actualLabelTitle.text = title
actualLabelTitle.numberOfLines = 0
actualLabelTitle.minimumScaleFactor = 0.1
}
// Get the news date and assign to the label
if let actualLabelDate = labelDate {
let character = "| "
actualLabelDate.text = character + currentNewToDisplay.date_short
}
// Get the news section and assign to the label
if let actualabelSection = labelSection {
actualabelSection.text = currentNewToDisplay.section
}
}
case 1:
// If segment is 1, take data from toprated News
if (indexPath.row <= self.topratedNews.count - 1){
let currentNewToDisplay2 = self.topratedNews[indexPath.row]
// Get the image and assign to the imageView
if let actualImageView2 = imageView {
// Imageview actually exists
if currentNewToDisplay2.imageUrl != "" {
// Image url exists, so download it
let imageUrl2:NSURL? = NSURL(string: currentNewToDisplay2.imageUrl)
// Download image with SDWebImage library
if let url2 = imageUrl2 {
actualImageView2.sd_setImageWithURL(url2)
}
}
}
// Get the news title and assign to the label
if let actualLabelTitle2 = labelTitle {
actualLabelTitle2.text = currentNewToDisplay2.title
actualLabelTitle2.numberOfLines = 0
actualLabelTitle2.minimumScaleFactor = 0.1
}
// Get the news date and assign to the label
if let actualLabelDate2 = labelDate {
let character2 = "| "
actualLabelDate2.text = character2 + currentNewToDisplay2.date_short
}
// Get the news section and assign to the label
if let actualabelSection2 = labelSection {
actualabelSection2.text = currentNewToDisplay2.section
}
}
case 2:
if (indexPath.row <= self.latestNews.count - 1){
// If segment is 2, take data from latestNews News
let currentNewToDisplay3 = self.latestNews[indexPath.row]
// Get the image and assign to the imageView
if let actualImageView3 = imageView {
// Imageview actually exists
if currentNewToDisplay3.imageUrl != "" {
// Image url exists, so download it
let imageUrl3:NSURL? = NSURL(string: currentNewToDisplay3.imageUrl)
// Download image with SDWebImage library
if let url3 = imageUrl3 {
actualImageView3.sd_setImageWithURL(url3)
}
}
}
// Get the news title and assign to the label
if let actualLabelTitle3 = labelTitle {
actualLabelTitle3.text = currentNewToDisplay3.title
actualLabelTitle3.numberOfLines = 0
actualLabelTitle3.minimumScaleFactor = 0.1
}
// Get the news date and assign to the label
if let actualLabelDate3 = labelDate {
let character3 = "| "
actualLabelDate3.text = character3 + currentNewToDisplay3.date_short
}
// Get the news section and assign to the label
if let actualabelSection3 = labelSection {
actualabelSection3.text = currentNewToDisplay3.section
}
}
default:
break
}
// Set insets to zero
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
// MARK: Segmented Control
#IBAction func segmentedChanged(sender: UISegmentedControl) {
switch self.sectionSegmentedControl.selectedSegmentIndex {
case 0:
// If segment is 0, return rows for coverNews array
if (self.coverNews.count == 0) {
loadNews()
}else{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
})
}
case 1:
// If segment is 1, return rows for topratedNews array
if (self.topratedNews.count == 0) {
loadNews()
}else{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
})
}
case 2:
// If segment is 2, return rows for latestNews array
if (self.latestNews.count == 0) {
loadNews()
}else{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
})
}
default:
break
}
}
// MARK: Load News
func loadNews(){
switch(sectionSegmentedControl.selectedSegmentIndex){
case 0:
self.model.getFeedNews("cover")
case 1:
self.model.getFeedNews("toprated")
case 2:
self.model.getFeedNews("latest")
default:
break
}
}