Custom Input View in Swift
Asked Answered
P

2

6

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)?

Pigtail answered 28/10, 2014 at 5:13 Comment(4)
Create a custom class file and set this as the class for your xib in InterfaceBuilder. Then you can ctrl-drag from UI elements to your class file (assistant editor) to create IBOutlets. Use this outlets in your code to update the UI elements.Bigwig
I've been able to create IBOutlets/IBActions for the buttons in the customView. This has been necessary but not sufficient to get buttons in the customView to communicate textInput/keyboardFunctionality to the view of TextInputs.Pigtail
don't your views have to implement/conform to the proper protocol? (UITextInput?). you might also have to tell them to become first responder when they are tapped. I'd try subclassing UIControl vs. UIViewKickoff
hav a looke at this: #26540349.Bloat
B
8

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: An example of how it could look

Bountiful answered 15/2, 2015 at 10:34 Comment(0)
T
9

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: "")
            }
        }
    }
Tutorial answered 17/2, 2015 at 17:23 Comment(2)
Thanks so much for this answer, David. If I wanted to re-use the keyboard across a few places, what would you recommend as the best way of doing that?Egypt
You could subclass UIViewController or you could instantiate this code in either a UITextField subclass or a separate controller class.Tutorial
B
8

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: An example of how it could look

Bountiful answered 15/2, 2015 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.