0

I am currently programming a Mind Mapping app, where the app user can add textfields wherever they want to on a blank page. Is there a way to do something like that. An unprofessional way I came up with, would be adding a few hundred empty textfields to the ViewController, that the user can then fill in. However I am sure there is a better way. Would be nice if the user could tap on a "Add Text" button to generate a Text Field (just like in Microsoft Word) that can be moved around on the blank page. I really cannot think of any class that could solve such a task. Can anybody help?

Cheers

TAMM
  • 11
  • 4

1 Answers1

1

You can always create a text field programatically. In your ViewController file, you will need to create an instance of UITextField. For information on UITextField, check out this documentation.

For example, on iOS, when a user touches the screen with one or more fingers, methods are called on the View Controller. When a user touches the screen, iOS calls touchesBegan and touchesEnded on the View Controller. In your case, you probably want touchesEnded, so that the field will not be added until the user lifts his/her finger. These methods pass a Set of UITouch's. UITouch Class Reference

Example View Controller code in Swift:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        print("Touch Ended")

        for touch in touches {

            let touchPoint = touch.locationInView(self.view)

            let fieldFrame = CGRect(x: touchPoint.x, y: touchPoint.y, width: 100, height: 30)

            let textField = UITextField(frame: fieldFrame)
            textField.placeholder = "Type Here"

            view.addSubview(textField)

        }
    }

}

You may already be aware of how to programmatically layout views, but…

This code finds the location of the touch and makes that location the textfield's origin. Here, I am simply setting the frame for the textField before adding it the view, hardcoding a width of 100 and height of 30. In practice, you may want to look into something like AutoLayout, particularly NSLayoutConstraint, which allows for programmatic constraints that account for bounds changes. You also may not want to add a text field for each touch if they touch the screen with multiple fingers at once.

This allows a field to be placed wherever the user touches. Alternatively, you could create a button to add a text field like you said and then just come up with a default location for the UITextField to be added instead of observing touches.

Dragging a text field

To move the field when the user drags a text field, you will want to observe a touch, and check for a textfield at the touch, which is essentially checking for a subview at a point.

Then, you can use touchesMoved to observe a drag and update the location of the textfield via AutoLayout or whatever procedure you decide to use.

Gesture Recognizer's

As an alternative to touchesBegan, touchesMoved, touchesEnded, you can use gesture recognizers. See information on the different types of UITapGestureRecognizer's from Apple's documentation. These can be created by specifying a target and action method, then added to a view. Example of creating a tap gesture recognizer and adding it to a UITextField stored in a variable called textfield.

let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("textFieldTapped:"))
textfield.addGestureRecognizer(tapRecognizer)
Community
  • 1
  • 1
Matthew Seaman
  • 7,952
  • 2
  • 37
  • 47
  • You are an absolute genius! Thanks a lot for this extensive and understandable explanation. Unfortunately I can't yet upvote your comment (no rep), but I am stil very thankful :) – TAMM Dec 28 '15 at 12:43
  • I really liked the idea but using touchesEnded made it very difficult to do other gesture actions, which is why i decided to use ?`UITapGestureRecognizer` to solve the task. (So I can also use `PinchGesture` later and especially because I hope it's easier to drag the textfields around with the `PanGesture`) Unfortunately, that didn't really work out haha. This is how I set up my ViewController; `override func viewDidLoad() { let tapGR = UITapGestureRecognizer(target: self, action: "didTap:")` I don't really know what's wrong with it because until now it worked properly. – TAMM Dec 30 '15 at 22:29
  • The error says; Thread 1: breakpoint 1.1` and happens right after successfully building the project. – TAMM Dec 30 '15 at 22:33
  • Have you set a breakpoint in Xcode? Look for a blue mark next to one of the lines. If you see it, you can right-click and delete it. As for [UITapGestureRecognizer](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITapGestureRecognizer_Class/index.html#//apple_ref/occ/cl/UITapGestureRecognizer), you should read the documentation linked. Right now, "didTap:" should fire whenever the user taps anywhere. If you find Gesture recognizers useful, I'll update my answer for them. Also note it may be easier to set up a gesture recognizer in the storyboard. – Matthew Seaman Dec 31 '15 at 04:24
  • Yes thank you very much, the breakpoint was the problem ;) The code for the UITapRecognizer worked actually. I know, that would be easier. But is there a way to add a UITapGestureRecognizer for an object that doesn't exist yet? Because usually I would manually add an object to the ViewController in the storyboard and then link a GestureRecognizer with that specific object. Objects that are added when using the app on my device won't react on the gestures. Cheers – TAMM Dec 31 '15 at 13:10
  • After you have created the UITapGestureRecognizer programmatically, you can call `textfield.addGestureRecognizer(tapGR)`. Keep in mind that a single UITapGestureRecognizer can only be added to a single object at a time, so you will need to create a new one for each textfield if you want one on every textfield. The `target` you specify when you create one is just the class you want the message sent to. – Matthew Seaman Jan 01 '16 at 07:49