I have UITableView with about 20 row which each contain a UITextField. The first time I tap in a textfield will open the keyboard and I am ready to edit this textfield. If I tap on the next textfield (notice the keyboard is displayed all the time) the keyboard is still displayed but the blue cursor is not in the new textfield and I cannot enter any text. But if I tap on another textfield again, it works just fine. This behavior occurs alternately, one time it works the other time it doesn't.
The delegate method textFieldShouldBeginEditing(_:) is always called, wether I can edit or not. The delegate method textFieldDidBeginEditing(_:) is only called when editing works.
This is the code for cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextFieldCell")!
let titleLabel = cell.viewWithTag(1) as! UILabel
let contentTextField = cell.viewWithTag(2) as! FixableTextField
contentTextField.delegate = self
contentTextField.inputAccessoryView = doneToolbar
// Enable/disable editing for text fields
if isEditing {
contentTextField.enableEditing()
} else {
contentTextField.disableEditing()
}
// Present Profile Data
if profileUpdateBuffer != nil {
switch indexPath.row {
case 0:
titleLabel.text = "Count"
contentTextField.text = "\(profileUpdateBuffer!.count)"
contentTextField.purposeID = "count"
contentTextField.keyboardType = .numberPad
case 1:
titleLabel.text = "City"
contentTextField.text = "\(profileUpdateBuffer!.city)"
contentTextField.purposeID = "city"
contentTextField.keyboardType = .default
// ...
case 20:
titleLabel.text = "Name"
contentTextField.text = "\(profileUpdateBuffer!.name)"
contentTextField.purposeID = "name"
contentTextField.keyboardType = .default
default:
titleLabel.text = ""
contentTextField.text = ""
}
return cell
}
// No data available -> show info in first row
else {
if indexPath.row == 0 {
titleLabel.text = "No data"
contentTextField.text = "No data"
}
else {
titleLabel.text = ""
contentTextField.text = ""
}
return cell
}
}
The enableEditing() and disableEditing() method are from class FixableTextField. I can see that the textfields are always enabled because I can see the textfield border
// Extract from FixableTextField class
func enableEditing() {
self.isEnabled = true
self.borderStyle = .roundedRect
}
func disableEditing() {
self.isEnabled = false
self.borderStyle = .none
}
Code for the UITextField
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
// Delete empty field indicator "-"
if textField.text == "-" {
textField.text = ""
}
//Move profileTable's contentView to correct position
if textField is FixableTextField {
let path = IndexPath(row: rowMap[(textField as! FixableTextField).purposeID]!, section: 0)
moveContentViewUp(indexPath: path)
}
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
// Save new value to profileUpdateBuffer
do {
try self.profileUpdateBuffer?.setProperty(value: textField.text!, key: (textField as! FixableTextField).purposeID)
} catch ProfileError.PropertySettingWrongType {
let falseInputAlert = UIAlertController(title: "False Input", message: "The input for this field is not valid.", preferredStyle: .alert)
falseInputAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(falseInputAlert, animated: true, completion: nil)
} catch {
print("Error when trying to set property for profileUpdateBuffer in ProfileViewController")
}
// Display new data in table
profileTable.reloadData()
}
Extract from setProperty method which is from class ProfileData. profileUpdateBuffer is of type ProfileData
func setProperty(value:String, key:String) throws {
switch key {
case "count":
count = value
case "city":
count = value
// ...
case "name":
name = value
default:
throw ProfileError.PropertySettingWrongType
}
}
I've made a small program to mimic the behavior you describe.
It seems the issue is caused by table view data reloading at the end of your textFieldDidEndEditing(_:):
func textFieldDidEndEditing(_ textField: UITextField) {
// Save new value to profileUpdateBuffer
do {
try self.profileUpdateBuffer?.setProperty(value: textField.text!, key: (textField as! FixableTextField).purposeID)
} catch ProfileError.PropertySettingWrongType {
let falseInputAlert = UIAlertController(title: "False Input", message: "The input for this field is not valid.", preferredStyle: .alert)
falseInputAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(falseInputAlert, animated: true, completion: nil)
} catch {
print("Error when trying to set property for profileUpdateBuffer in ProfileViewController")
}
// Display new data in table
profileTable.reloadData()
}
Try removing profileTable.reloadData() for the sake of experiment to confirm the root cause of the problem (yes, your other cells will not be updated).
One way to solve this is by utilizing direct cell updates on visibleCells in textFieldDidEndEditing(_:). I see profileUpdateBuffer? is your data model. Just update your cell's titleLabel and textField properties manually from your model if they are in visible cells property of the table view.
If you want to size the cells accordingly, use AutoLayout and UITableViewAutomaticDimension for table view row height combined with beginUpdates()/endUpdates() calls.
For more details on how to achieve direct cell manipulation and/or dynamic cell size update without loosing the keyboard focus check the accepted answer on this question I've already answered.
Hope this will help!
I believe the problem is that you call profileTable.reloadData()... You should probably only reload one cell. Perhaps textFieldDidEndEditing(:) gets called with the old TextField as a parameter, followed by textFieldShouldBeginEditing(:) for the new TextField. The problem is that you refresh all the cells at the end of textFieldDidEndEditing(:), which means the TextField passed by the system as a parameter to textFieldShouldBeginEditing(:) may not necessary be the same one that the corresponding cell contains... It may be that those new key-strokes get sent to a TextField belonging to a cell that is in the Queue of reusable cells, i.e. not visible, but still existing somewhere in memory.
Related
EDIT 2: Thought I had it but I don't. I need to create the array when the button is pressed, but I can't figure out how to access the .checkmark for each object. I have ".selected" as a property already, but can't access that either.
I have a UITableView that display a list in three sections with checkmarks. When a row in checked, the bool associated with that NSObject class changes from false to true. I have a button in the UINavigationBar that when pressed shows an alert with "Send" and "Cancel" options. When the user taps "Send" I want to have all the rows with checkmarks added into an array. I figured using that bool would be the best way, but in the func for the action I can't call the bool associated with each NSObject in the array. I included the parts of code I think are needed to help me.
EDIT: I believe this is how I have my class set up, unless I'm misunderstanding. One of the class' vars is "var selected: Bool" then when I create an Item from that class I set it to false. I have a list of Items that displays fine in cellForRowAt but when the button is tapped I can't get access to that same "sortedList". Here's more code. Maybe I'm still missing something else?
var arrayOfItemsSelected = [String]()
var sortedItems = [[Item]]()
var itemList: ItemList! {
didSet {
sortedItems = itemList.sortByBrands()
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = sortedItems[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "UIItemViewCell", for: indexPath)
cell.textLabel?.text = "\(item.name)"
cell.detailTextLabel?.text = "\(item.style)"
cell.accessoryType = cell.isSelected ? .checkmark : .none
cell.selectionStyle = .none // to prevent cells from being "highlighted"
return cell
}
func confirmButtonPressedAction() {
// Create the alert controller
let alertController = UIAlertController(title: "List of checked items", message: stringOfSelectedItems, preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "Send", style: UIAlertActionStyle.default) {
UIAlertAction in
self.arrayOfItemsSelected.removeAll()
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel) {
UIAlertAction in
}
// Add the actions
alertController.addAction(okAction)
alertController.addAction(cancelAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
}
Your code either isn't doing anything with arrayOfItemsSelected or you're not showing us the relevant parts. As it is, the code sample will neither obtain nor process the selected items.
You could either implement the didSelectRowAt and didDeselectRowAt function to maintain an internal flag in your Item class, or you could get the selection from indexPathsForSelectedRows.
If you want to completely circumvent the UITableView's selection mechanism, you would need to handle cell clicking/tapping with actions in the UIItemViewCell itself (but that seems overly complicated).
BTW, I am doubtful that you can rely on cell.isSelected from a cell you just dequeued. Also, resorting and reloading the tableview in a didSet closure is likely to get you into trouble causing performance issues, random crashes or infinite loops.
If you have a property checked on the class Item you can try:
let itemsChecked = sortedItems.filter {
$0.checked == true
}
You have to add boolean check with each array object.link below
For Objective C:
#[
#{
#"Name":#"abc",
#"Status":[NSNumber numberWithBool:YES]
}
]
For Swift:
[
[
"Name": "abc",
"Status": true
]
]
You have to add boolean check with each array object, for example myModel.
[
"Name": "abc",
"Status": true
]
When a row in checked,
myModel.Status = myModel.Status ? false : true
myModel's default state = false
then " you can call the bool associated with each NSObject in the array"
I have a Table view, Text field and a update Button. Table view cells count are determined by the user input in the Text field. Table view cells contains only a text field, in which user can enter some data.
What I want is, I want to store the user entered data from table view cells into an array when click on that update button. How to do this? What is the best method for this?
This is the code in update button ;
#IBAction func onUpdateButtonClick(sender: AnyObject) {
let tableViewLength = Int(productNumberTxtField.text!)! // TextField data which decides the table view cell count
for index in 0...(tableViewLength-1)
{
let indexPath = NSIndexPath(forRow: index, inSection: 0)
let cell = self.productsTableView.cellForRowAtIndexPath(indexPath) as? ProductCell
if(cell!.productTextField.text != "") { // productTextField is the text field in cell
UserDataController.sharedInstance.saveToProductsCoreData(userName, productName: (cell!.productTextField.text)!)
}
}
}
declare array
var setArray : [String] = []
first of while making textfield you have to give tag and delegate.
textfield.tag = indexPath.row
textfield.delegete = self
than in textfield delegate method
func textFieldDidEndEditing(_ textField: UITextField) {
if(textField.text != ""){
setArray[textField.tag] = textField.text! as String
print(setArray)
}
}
uploaded video (7 sec) for describing my problem
i have a textView embed in a tableView cell when we enter something on that textView the textview automatically increases its height according to entered content and also cell increases its size for doing this am using this approach:-
//tableViewCell
/// Custom setter so we can initialise the height of the text view
var textString: String {
get {
return textView?.text ?? ""
}
set {
if let textView = textView {
textView.text = newValue
textViewDidChange(textView)
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Disable scrolling inside the text view so we enlarge to fitted size
textView?.scrollEnabled = false
textView?.delegate = self
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
textView?.becomeFirstResponder()
} else {
textView?.resignFirstResponder()
}
}
}
extension MultiLineTextInputTableViewCell: UITextViewDelegate {
func textViewDidChange(textView: UITextView) {
let size = textView.bounds.size
let newSize = textView.sizeThatFits(CGSize(width: size.width, height: CGFloat.max))
// Resize the cell only when cell's size is changed
if size.height != newSize.height {
UIView.setAnimationsEnabled(false)
tableView?.beginUpdates()
tableView?.endUpdates()
UIView.setAnimationsEnabled(true)
if let thisIndexPath = tableView?.indexPathForCell(self) {
tableView?.scrollToRowAtIndexPath(thisIndexPath, atScrollPosition: .Bottom, animated: false)
}
}
}
here everything is fine but i added a function for Forwarding any content and for this am using this approach :-
forwardNoteAction = UITableViewRowAction(style: .Normal, title: "Forward"){ action, index in
let cell = tableView.cellForRowAtIndexPath(indexPath) as! NotesTableViewCell
if self.searchBar.text == "" {
let object: PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
let postsObjID = object.objectId!
let query = PFQuery(className: "Notes")
query.fromLocalDatastore()
query.getObjectInBackgroundWithId(postsObjID) {
(objectViewes, error) -> Void in
if error != nil {
print(error)
} else {
self.forwardObject = object
print(self.forwardObject)
//object.pinInBackground()
self.performSegueWithIdentifier("forwardNoteSegue", sender: nil)
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
i am putting the content into a variable self.forwardObject and passing that variable with an segue forwardNoteSegue after passing self.forwardObject am using that content for filling my tableView (which is a form , consist of textViews and TextFields) but when am doing this my textView is not increasing its height according to content which is inside that textView ,
all i want is the textView should increase increase its height when we use "textView.text = copiedContent" same as when we enter anything into the textView manually
please for understanding my problem properly see the uploaded video (link is provided above)
if my question is not understandable than please let me know i'll fix it
thanks
You need to manually call textViewDidChange when you modify the content of you textview programatically.
This is documented in the UITextviewDelegate's description of the method:
The text view calls this method in response to user-initiated changes to the text. This method is not called in response to
programmatically initiated changes.
I am having some serious trouble getting my multiline label to show all of its lines. Often, the last line of the label simply does not appear, but it is apparent that the dynamically calculated cell height has taken in to account that it should have appeared, leaving around the appropriate amount of white space left over in my cell.
The affected label can display 1-7 lines depending on the data. I have played around with many various constraints to try and get it to display but regardless of what is on the last line, it just won't display.
The weird thing is, sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display. The opposite happens frequently too (last line of label cutting off when I segue in to the VC, but then using the segmented controller inside the VC to change the displayed data and then go back, it will then display fine).
Things I have ensured: The label is set to word wrap, has line count of 0, has a height greater than or equal to the height of one of it's lines, its resistance and vertical content hugging is set to the highest of anything in the cell, and the width is set appropriately.
The below code is how I determine how many lines the label will have:
let descString = NSMutableAttributedString()
let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: bounty.description)
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)
let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(2)])
descString.appendAttributedString(bLine)
if !(bounty.turtles == 0.0){
let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
let turtleAmount = NSMutableAttributedString(string: bounty.turtleCount.description)
descString.appendAttributedString(turtleStart)
descString.appendAttributedString(turtleAmount)
}
descriptionLabel.attributedText = descString
In the screen shot below, you can see that the height of the cell is being calculated appropriately but for some reason, the last line of the label just refuses to show. It should appear after the "is for noobs" line. I've manipulated the white space to appear after the line instead of elsewhere by setting the problem labels bottom to constraint to be greater than or equal, and all other top to bottom constraints as equal to.
Constraints of the problem label:
I've been stumped for quite a while on this one, and I'm starting to think it's not the constraints I've set but something much deeper. All though I would love to be proven wrong.
Here is my VC code.
import UIKit
class TrendingVC: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var menubtn:UIBarButtonItem!
#IBOutlet var trendingTableView:UITableView!
var trendingToggle:Int = 0
let nwt = NWTrending()
let appUserId = NSUserDefaults.standardUserDefaults().stringForKey("UserId") ?? "1" //#TODO: remove ?? 1
var bountyArr: [Bounty] = []
var compArr: [Completion] = []
var peopleArr: [Person] = []
var userId: String = "0"
var username: String = ""
let bountyCellIdentifier = "BountyCellNew"
let personCellIdentifier = "PersonCell"
let completedCellIdentifier = "TrendingCompletedImageCell"
#IBAction func toggleTrending(sender:UISegmentedControl){
switch sender.selectedSegmentIndex{
case 0:
//loads the bounties on segmented control tab
trendingToggle=0
nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in //#TODO: change pos
self.bountyArr = bountyArr as [Bounty]
self.reloadTableViewContent()
}
case 1:
trendingToggle=1
nwt.getTrendingCompletions(appUserId, position: 0){(compArr, err) in
self.compArr = compArr as [Completion]
self.reloadTableViewContent()
}
case 2:
trendingToggle=2
nwt.getTrendingPeople(appUserId, position: 0){(peopleArr, err) in
self.peopleArr = peopleArr as [Person]
self.reloadTableViewContent()
}
default:
break
}
//reloadTableViewContent()
}
override func viewDidLoad() {
super.viewDidLoad()
trendingTableView.estimatedRowHeight = 300.0
trendingTableView.rowHeight = UITableViewAutomaticDimension
/******* Kyle Inserted *******/
//for followers and following back button text, you set it here for when you segue into that section
let backItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backItem
/******* END Kyle Inserted *******/
trendingTableView.allowsSelection = false;
trendingTableView.delegate = self
trendingTableView.dataSource = self
//sidebar code
if self.revealViewController() != nil {
menubtn.target = self.revealViewController()
menubtn.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
//loads the bounty on segue
nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in
self.bountyArr = bountyArr as [Bounty]
self.reloadTableViewContent()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//deselectAllRows()
}
func deselectAllRows() {
if let selectedRows = trendingTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
for indexPath in selectedRows {
trendingTableView.deselectRowAtIndexPath(indexPath, animated: false)
}
}
}
func reloadTableViewContent() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.trendingTableView.reloadData()
println("reloading table view content")
self.trendingTableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: false)
})
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(trendingTableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if trendingToggle == 0{
return bountyArr.count
}
else if trendingToggle == 1{
return compArr.count
}
else {
return peopleArr.count
}
}
func tableView(trendingTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if trendingToggle == 0{
return bountyCellAtIndexPath(indexPath)
}
else if trendingToggle == 1{
return completedCellAtIndexPath(indexPath)
}
else{
return personCellAtIndexPath(indexPath)
}
}
//calls method to set and display each trending bounty cell
func bountyCellAtIndexPath(indexPath:NSIndexPath) -> BountyCellNew {
let cell = trendingTableView.dequeueReusableCellWithIdentifier(bountyCellIdentifier) as! BountyCellNew
var bounty = bountyArr[indexPath.row]
cell.setBountyCellTrending(bounty)
return cell
}
func completedCellAtIndexPath(indexPath:NSIndexPath) -> CompletedCell{
let cell = trendingTableView.dequeueReusableCellWithIdentifier(completedCellIdentifier) as! CompletedCell
var comp = compArr[indexPath.row]
cell.setTrendingCompletedCell(comp)
return cell
}
func personCellAtIndexPath(indexPath:NSIndexPath) -> PersonCell{
let cell = trendingTableView.dequeueReusableCellWithIdentifier(personCellIdentifier) as! PersonCell
var peop = peopleArr[indexPath.row]
cell.setTrendingPeopleCell(peop)
return cell
}
}
Unable to comment due to rep low.
This seems like a IB thing, very likely constraints.
Make sure that any component's top constraint placed under this multiline UILabel is set to the bottom of said UILabel.
For example, in this project of mine, both the TitleView and TimeAndDetailsView contain UILabels that span multiplelines. In order to allow autolayout to properly space things all constraints need to be in order, also notice how the top constraint of TimeAndDetails is the bottom of TitleView
Edit note:
Before you tableView.reloadData() type the following 2 lines, or if uncertain, put it inside viewDidLoad
tableView.estimatedRowHeight = 60 //Type your cell estimated height
tableView.rowHeight = UITableViewAutomaticDimensionhere
Went to sleep last night, ok so I decided to add your code to my details label from the storyboards picture I had posted before, and it seems to have displayed just fine. I have a few questions and pointers below.
let descString = NSMutableAttributedString()
let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: "this would be 'bounty.description and is set to size 40", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(40)])
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)
let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(10)])
descString.appendAttributedString(bLine)
let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
let turtleAmount = NSMutableAttributedString(string: "random number 1234 goes here")
descString.appendAttributedString(turtleStart)
descString.appendAttributedString(turtleAmount)
detailsLabel.attributedText = descString
Sadly, this is in a scrollview and not in a cell, so there is the first difference. My first question; is your descriptionLabel.attributedText supposed to display different fontscolors or fonttypes/sizes?, if not then I would say to just use descriptionLabel.text instead of descriptionLabel.attributedText.
Now the fact that there is white space but nothing showing us makes me wonder if bounty.turtleCount.description was previously set to color white somewhere else, or you are simply seeing the newlines you added \n\n and nothing is there because if !(bounty.turtles == 0.0) doesnt execute. Are you sure the that if statement is executed? place a breakpoint and follow along to make sure the values are appended to descString
If this is all correct, then my guess would still be on constraints.
Could you elaborate on, quote from you; "sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display." what are you doing different in the seg control when loading the values and reloading the tableview that makes it not display, and under what conditions does it work properly when you segue.
I updated xcode and now this doesn't happen anymore.
I've spent hours trying to figure out how to create/then get a custom inputView to work.
I have a grid of TextInputs (think scrabble board) that when pressed should load a custom inputView to insert text.
I've created a .xib file containing the UI elements for the custom inputView. I was able to create a CustomInputViewController and have the inputView appear but never able to get the actual TextInput to update it's value/text.
Apple documentation has seemed light on how to get this to work and the many tutorials I've have seen have been using Obj-C (which I have been unable to convert over due to small things that seem unable to now be done in swift).
What is the overarching architecture and necessary pieces of code that should be implemented to create a customInputView for multiple textInputs (delegate chain, controller, .xibs, views etc)?
Set up a nib file with the appropriate inputView layout and items. In my case I set each button to an action on File Owner of inputViewButtonPressed.
Set up a storyboard (or nib if you prefer) for a view controller.
Then using the following code, you should get what you're looking for:
class ViewController: UIViewController, UITextFieldDelegate {
var myInputView : UIView!
var activeTextField : UITextField?
override func viewDidLoad() {
super.viewDidLoad()
// load input view from nib
if let objects = NSBundle.mainBundle().loadNibNamed("InputView", owner: self, options: nil) {
myInputView = objects[0] as UIView
}
// Set up all the text fields with us as the delegate and
// using our input view
for view in self.view.subviews {
if let textField = view as? UITextField {
textField.inputView = myInputView
textField.delegate = self
}
}
}
func textFieldDidBeginEditing(textField: UITextField) {
activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeTextField = nil
}
#IBAction func inputViewButtonPressed(button:UIButton) {
// Update the text field with the text from the button pressed
activeTextField?.text = button.titleLabel?.text
// Close the text field
activeTextField?.resignFirstResponder()
}
}
Alternatively, if you're wanting to be more keyboard-like, you can use this function as your action (it uses the new let syntax from Swift 1.2), break it up if you need 1.1 compatibility:
#IBAction func insertButtonText(button:UIButton) {
if let textField = activeTextField, title = button.titleLabel?.text, range = textField.selectedTextRange {
// Update the text field with the text from the button pressed
textField.replaceRange(range, withText: title)
}
}
This uses the UITextInput protocol to update the text field as appropriate. Handling delete is a little more complicated, but still not too bad:
#IBAction func deleteText(button:UIButton) {
if let textField = activeTextField, range = textField.selectedTextRange {
if range.empty {
// If the selection is empty, delete the character behind the cursor
let start = textField.positionFromPosition(range.start, inDirection: .Left, offset: 1)
let deleteRange = textField.textRangeFromPosition(start, toPosition: range.end)
textField.replaceRange(deleteRange, withText: "")
}
else {
// If the selection isn't empty, delete the chars in the selection
textField.replaceRange(range, withText: "")
}
}
}
You shouldn't go through all that hassle. There's a new class in iOS 8 called: UIAlertController where you can add TextFields for the user to input data. You can style it as an Alert or an ActionSheet.
Example:
let alertAnswer = UIAlertController(title: "Input your scrabble Answer", message: nil, preferredStyle: .Alert) // or .ActionSheet
Now that you have the controller, add fields to it:
alertAnswer.addTextFieldWithConfigurationHandler { (get) -> Void in
getAnswer.placeholder = "Answer"
getAnswer.keyboardType = .Default
getAnswer.clearsOnBeginEditing = true
getAnswer.borderStyle = .RoundedRect
} // add as many fields as you want with custom tweaks
Add action buttons:
let submitAnswer = UIAlertAction(title: "Submit", style: .Default, handler: nil)
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertAnswer.addAction(submitAnswer)
alertAnswer.addAction(cancel)
Present the controller whenever you want with:
self.presentViewController(alertAnswer, animated: true, completion: nil)
As you see, you have various handlers to pass custom code at any step.
As example, this is how it would look: