UILabel text fade out one by one - ios

I want to figure out how to fade out text one by on in UILabel.
For example:
label.text = "abcdefg", I want to let the text fade out one by one -> 'a','b','c','d','e','f','g' fade out respectively.
How could I do this?
Thanks for all your answers, I want to clarify that I want an animation to fade out, something like UIViewAnimationOptions.CurveEaseInOut.
Thanks!

Use 2 labels and fade them in and out repeatedly, removing characters from each between each fade.
first shown with abcdefg second hidden with abcdef
fade first out and second in
first hidden with abcde second shown with abcdef
fade first in and second out
etc
Or, use the view capture SDK to create images of your label with different text content and then animate through the images with some transition.

You can do it with using NSTimer and remove the last letter from string of its intervention. For example you can do something like this:
Simple class for fade text animation
class TextFadeAnimation {
var text: String
var timer: NSTimer?
var block: ((text: String) -> Void)?
init(text: String) {
self.text = text
}
func startAnimation(resultTextBlock block: (text: String) -> Void) {
self.block = block
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("fadeOut"), userInfo: nil, repeats: true)
}
func stopAnimation() {
if timer != nil {
timer!.invalidate()
}
}
#objc private func fadeOut() {
if count(text) > 1 {
text = text.substringToIndex(text.endIndex.predecessor())
block!(text: text)
} else {
timer!.invalidate()
block!(text: "")
}
}
}
Use example in ViewController
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var textFadeAnimation: TextFadeAnimation?
override func viewDidLoad() {
super.viewDidLoad()
textFadeAnimation = TextFadeAnimation(text: "abcdefg")
textFadeAnimation!.startAnimation(resultTextBlock: { (text: String) -> Void in
self.label.text = text
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Related

Updating label in time shows up all at once

I have a label that I would like to populate with a number of dots, each dot appearing in sequence, separated by 0.1 second
func setUpDots(numberOfDots: Int) {
for dots in 1...numberOfDots {
DispatchQueue.global(qos: .userInteractive).async {
DispatchQueue.main.async {
self.setLabelToDots(numberOfDots: dots)
}
usleep(100000) // wait 0.1 sec between showing each dot
}
}
}
func setLabelToDots(numberOfDots: Int) {
let dots = Array( repeating: ".", count: numberOfDots).joined()
myLabel.text = dots
myLabel.setNeedsDisplay()
}
But all the dots appear on the label at once
What should I do to get the right effect of the dots showing up with the specified delay between them?
Thanks for your feedback.
Basically, your for-loop is doing something similar to...
for dots in 1...numberOfDots {
self.setLabelToDots(numberOfDots: dots)
}
This is because each task is been allowed to execute at the same and the delay is having no effect on when the next one will run.
You "could" use a serial queue or you could use dependent operation queue, but a simpler solution might be to just use a Timer
This will allow you to setup a "delay" between ticks and treat the timer as a kind of pseudo loop, for example
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
let numberOfDots = 10
var dots = 0
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
myLabel.text = ""
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard timer == nil else {
return
}
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
}
#objc func tick() {
dots += 1
guard dots <= numberOfDots else {
timer?.invalidate()
timer = nil
dots = 0
return
}
numberOfDots(dots)
}
func numberOfDots(_ numberOfDots: Int) {
// You could just use string consternation instead,
// which would probably be quicker
let dots = Array( repeating: ".", count: numberOfDots).joined()
myLabel.text = dots
myLabel.setNeedsDisplay()
}
}
There are plenty of other examples about, but you should also have a look at the documentation for Timer

Counting up/down numbers animation

I have an UIPageViewController with a number in the center of each VC in it.
I want that when I swipe from view to view, the number will begin at 0 and count up until it gets to the correct number (or if the number is negative - count down) like in this gif:
https://d13yacurqjgara.cloudfront.net/users/345970/screenshots/2126044/shot.gif
How can I do that?
Thank you!
You can use NSTimer to achieve this.
Here is example project I created for you.
Create layout like this:
Then in your ViewController do like so:
import UIKit
class ViewController: UIViewController {
#IBOutlet var countingLabel: UILabel!
var number = 0
var destinationNumber = 30
var timer: NSTimer!
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.
}
#IBAction func startButtonTapped(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "countUp", userInfo: nil, repeats: true)
}
func countUp() {
if number < destinationNumber {
number += 1
countingLabel.text = "\(number)"
} else {
timer.invalidate()
}
}
}
It will work.
Do not overcomplicate with timers and invalidations, etc.
extension UILabel {
func countAnimation(upto: Double) {
let from: Double = text?.replace(string: ",", replacement: ".").components(separatedBy: CharacterSet.init(charactersIn: "-0123456789.").inverted).first.flatMap { Double($0) } ?? 0.0
let steps: Int = 20
let duration = 0.350
let delay = duration / Double(steps)
let diff = upto - from
for i in 0...steps {
DispatchQueue.main.asyncAfter(deadline: .now() + delay * Double(i)) {
self.text = "\(from + diff * (Double(i) / Double(delay)))"
}
}
}
}

My code seems to run too quickly

I am in the process of writing a Simon style memory game, the phase of the game where the program shows the user the current list of stuff to remember seems to run instantly.
The idea is to step through the list (in the code I have placed 1 of each item as debug data) and change the colour on screen for a set period then move to the next.
I thought using for each item in memory array and then call a simple procedure to check which one it is and then change colour for a set period then back to original.
The code I have added here will work if I put breaks in between the test change colour (grey) and the original colour. But for some reason the timer does not seem too work.
Any ideas ?
import UIKit
import Foundation
var gameisrunning = false
var playererror = false
var memoryArray = [Int]()
var currentScore = 0
var timer = NSTimer()
class ViewController: UIViewController {
#IBAction func startGameButton(sender: UIButton) {
if gameisrunning == false {
gameisrunning = true
memoryArray.append(1) //for debug
memoryArray.append(2) //for debug
memoryArray.append(3) //for debug
memoryArray.append(4) //for debug
print(memoryArray) //for debug
gameStart()
} else {
}
}
//these are to be implemented once i get the showing sequence sorted.
#IBAction func redButton(sender: UIButton) {
}
#IBAction func greenButton(sender: UIButton) {
}
#IBAction func yellowButton(sender: UIButton) {
}
#IBAction func blueButton(sender: UIButton) {
}
#IBOutlet weak var redLabel: UILabel!
#IBOutlet weak var greenLabel: UILabel!
#IBOutlet weak var yellowLabel: UILabel!
#IBOutlet weak var blueLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
func addAnotherItemToMemory () {
// adds another item to the memory
memoryArray.append(Int(arc4random_uniform(4)+1))
}
func gameStart () {
// main body of game
showPlayerTheMemory()
}
func showPlayerTheMemory () {
// go through list and highlight the colors one at a time
for eachItem in memoryArray {
self.showColor(eachItem)
}
}
func pauseForAWhile(length: Double) {
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: nil , userInfo: nil, repeats: false)
timer.invalidate()
}
func showColor(buttonItem: Int) {
//check to see which color, change to grey (test color) and back to original after a set time.
if buttonItem == 1 {
self.redLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
self.redLabel.backgroundColor = UIColor.redColor()
print(buttonItem) //for debug
} else if buttonItem == 2 {
self.greenLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
greenLabel.backgroundColor = UIColor.greenColor()
print(buttonItem) //for debug
} else if buttonItem == 3 {
self.yellowLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
yellowLabel.backgroundColor = UIColor.yellowColor()
print(buttonItem) //for debug
} else if buttonItem == 4 {
self.blueLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
blueLabel.backgroundColor = UIColor.blueColor()
print(buttonItem) //for debug
}
}
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.
}
}
New relevant code changed to :
func colorChange (){
self.redLabel.backgroundColor = UIColor.redColor()
self.blueLabel.backgroundColor = UIColor.blueColor()
self.yellowLabel.backgroundColor = UIColor.yellowColor()
self.greenLabel.backgroundColor = UIColor.greenColor()
}
func showColor(buttonItem: Int, length: Double) {
//check to see which color, change to grey (test color) and back to original after a set time.
if buttonItem == 1 {
self.redLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 2 {
self.greenLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 3 {
self.yellowLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 4 {
self.blueLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
}
}
I have been scratching head all day trying to solve this issue which is baffling me. I have copied the new latest code in below, please discard code above.
I have four labels coloured red blue green and yellow. The array which has test data of 4 3 2 1 inside needs to step through each item - change the colour of the label for x secs then return it to normal colour. I have tried NSTimer, I have tried the current delay as in the code attached. Am I missing something as to where I place the code - should it be under viewdidload ??? I have tried for loops and the current code example shows switch in case it acted differently - it didnt !!
What happens basically is simultaneously all labels go grey (test colour right now) and then all go original colour after the x sec delay.
I need some help before I go insane. I honestly know it is something basic but I just cannot figure it out.
import UIKit
import Foundation
var gameisrunning = false
var playererror = false
var memoryArray = [Int]()
var currentScore = 0
class ViewController: UIViewController {
#IBAction func startGameButton(sender: UIButton) {
if gameisrunning == false {
gameisrunning = true
memoryArray.append(4) //for debug
memoryArray.append(3) //for debug
memoryArray.append(2) //for debug
memoryArray.append(1) //for debug
print(memoryArray) //for debug
gameStart()
} else {
}
}
//these are to be implemented once i get the showing sequence sorted.
#IBAction func redButton(sender: UIButton) {
}
#IBAction func greenButton(sender: UIButton) {
}
#IBAction func yellowButton(sender: UIButton) {
}
#IBAction func blueButton(sender: UIButton) {
}
#IBOutlet weak var redLabel: UILabel!
#IBOutlet weak var greenLabel: UILabel!
#IBOutlet weak var yellowLabel: UILabel!
#IBOutlet weak var blueLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
func addAnotherItemToMemory () {
// adds another item to the memory
memoryArray.append(Int(arc4random_uniform(4)+1))
}
func gameStart () {
// main body of game
showPlayerTheMemory()
}
func delayProg (){
//attempt 100093287492 to get a delay in program
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
self.blueLabel.backgroundColor = UIColor.blueColor()
self.yellowLabel.backgroundColor = UIColor.yellowColor()
self.greenLabel.backgroundColor = UIColor.greenColor()
self.redLabel.backgroundColor = UIColor.redColor()
}
}
func showPlayerTheMemory () {
// go through list and highlight the colors one at a time
for var i=0; i <= memoryArray.count-1; i++ {
self.showColor(memoryArray[i])
}
}
func showColor(buttonItem: Int) {
//check to see which color, change to grey (test color) and back to original after a set time.
switch (buttonItem) {
case 1:
self.redLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 2:
self.greenLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 3:
self.yellowLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 4:
self.blueLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
default:
print("error")
}
}
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.
}
}
Here is an example of proper implementation of NSTimer()
var myTimer = NSTimer()
func startTimer() {
myTimer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "myFunction", userInfo: nil, repeats: true)
}
func myFunction() {
myTimer.invalidate()
//do other stuff
}
//the selector is "myFunction", this will be the name of the function that you wish to call every time the timer reaches its specified intervl
//the interval in this case is 10 seconds. In my experience NSTimer is good down to the second but is not precise enough beyond that
//repeats: true ... this will tell the timer to repeat its action consistently firing the selector each time the given time interval is reached. If repeat is set to false then the timer only fires once
//use myTimer.invalidate to stop the timer and to stop calling the selector.
be sure to invalidate your timer or set repeats: false to make sure it doesn't go forever. Make sure your selector is spelled exactly the same as your function. if your function is func myFunction() then the selector should be "myFunction". Make sure you specify a valid time interval, which is taken as seconds.

button.setTitle blanks out image view

Can anyone tell me why setting the button.setTitle(string, forState) function blanks out the UIImage?
If I comment out both lines that use the button.setTitle, the animation runs fine. When the lines are there, the animation disappears when I press the button.
The var cryButton: UIButton and the function UpdateImage refer to the same button, but I tried adding a second button and referencing the title in that, and it had the same effect. It blanks out the image.
I'm stumped :)
import UIKit
class ViewController: UIViewController {
var counter = 1
var numImages = 8
var toggle = true
var timer = NSTimer()
#IBOutlet weak var babyImage: UIImageView!
#IBOutlet weak var cryButton: UIButton!
#IBAction func UpdateImage(sender: AnyObject) {
if toggle == true {
timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("doanimation"), userInfo: nil, repeats: true)
cryButton.setTitle("Stop crying", forState: UIControlState.Normal)
toggle = false
} else {
timer.invalidate()
babyImage.image = UIImage(named: "WaWa1.png")
cryButton.setTitle("Wa-Waaaaa!", forState: UIControlState.Normal)
toggle = true
}
}
func doanimation() {
babyImage.image = UIImage(named: "WaWa\(counter).png")
if (counter == (numImages)) {
counter = 1
} else {
++counter
}
}
override func viewDidLayoutSubviews() {
babyImage.center = CGPointMake(babyImage.center.x - 400, babyImage.center.y)
}
override func viewDidAppear(animated: Bool) {
UIView.animateWithDuration(2, animations: { () -> Void in
self.babyImage.center = CGPointMake(self.babyImage.center.x + 400, self.babyImage.center.y)
})
}
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.
}
}
I finally find the problem. It is not caused by the timer, the timer is totally fine. the problem is caused by viewDidLayoutSubviews being called when you set the button title. You are using auto layout, and you are using the intrinsic content size for the button, right? And since the two title has different length so when you set the title, the button's frame will be changed, hence it will be layout again and then viewDidLayoutSubviews will be called. And you set the center value of the imageView inside viewDidLayoutSubviews, so basically when you set the title the image will move 400 points left to its original spot which I guess is out of the screen. Please note: if the image has different size, when you call imageView.image = UIImage(named: "image.png") will also call viewDidLayoutSubviews. I think all the images you are using has same size, thats why when the timer runs viewDidLayoutSubviews was not called. Here is a good answer which explains when does viewDidLayoutSubviews get called. I will let yourself to figure out the proper solution, OK? :)
Actually there is a better way to animate a UIImageView with an array of images, you could probably try this and remove the timer.
babyImage.animationImages = #[image1,image2,image3,image4,image5,image6,image7,image8]
babyImage.animationDuration = 1.6
// There are 8 images, so each image occupies 1.6 / 8 = 0.2 secs
babyImage.startAnimating()
// Use babyImage.stopAnimating() to stop the animation

Using "Next" as a Return Key

I use the "Next" value for the "Return Key" to get the Next button in place of the Done button, but (obviously) pressing it doesn't automatically move to the next UITextField in my view.
What's the right way to do this? I have seen many answers, but anyone have a swift solution?
Make sure your text fields have their delegate set and implement the textFieldShouldReturn method. This is the method that is called when the user taps the return key (no matter what it looks like).
The method might look something like this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == self.field1 {
self.field2.becomeFirstResponder()
}
return true
}
The actual logic in here might vary. There are numerous approaches, and I'd definitely advise against a massive if/else chain if you have lots of text fields, but the gist here is to determine what view is currently active in order to determine what view should become active. Once you've determined which view should become active, call that view's becomeFirstResponder method.
For some code cleanliness, you might consider a UITextField extension that looks something like this:
private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
}
}
}
And then change our textFieldShouldReturn method to look like this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.nextField?.becomeFirstResponder()
return true
}
Once you've done this, it should simply be a matter of setting each text field's new nextField property in viewDidLoad:
self.field1.nextField = self.field2
self.field2.nextField = self.field3
self.field3.nextField = self.field4
self.field4.nextField = self.field1
Although if we really wanted, we could prefix the property with #IBOutlet, and that would allow us to hook up our "nextField" property right in interface builder.
Change the extension to look like this:
private var kAssociationKeyNextField: UInt8 = 0
extension UITextField {
#IBOutlet var nextField: UITextField? {
get {
return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField
}
set(newField) {
objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN)
}
}
}
And now hook up the nextField property in interface builder:
(Set up your delegate while you're here too.)
And of course, if the nextField property returns nil, the keyboard just hides.
Here is an example in Swift:
I created a screen with 6 UITextFields. I assigned them the tags 1 through 6 in Interface Builder. I also changed the Return key to Next in IB. Then I implemented the following:
import UIKit
// Make your ViewController a UITextFieldDelegate
class ViewController: UIViewController, UITextFieldDelegate {
// Use a dictionary to define text field order 1 goes to 2, 2 goes to 3, etc.
let nextField = [1:2, 2:3, 3:4, 4:5, 5:6, 6:1]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Make ourselves the delegate of the text fields so that textFieldShouldReturn
// will be called when the user hits the Next/Return key
for i in 1...6 {
if let textField = self.view.viewWithTag(i) as? UITextField {
textField.delegate = self
}
}
}
// This is called when the user hits the Next/Return key
func textFieldShouldReturn(textField: UITextField) -> Bool {
// Consult our dictionary to find the next field
if let nextTag = nextField[textField.tag] {
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
// Have the next field become the first responder
nextResponder.becomeFirstResponder()
}
}
// Return false here to avoid Next/Return key doing anything
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
There is nothing wrong with the other answers, this is just a different approach with the benefit of being more focused on OOP - imho (although this is a bit more work up front, it can be reused). In the storyboard, I start off adding tags with a distinct range (e.g 800-810) that define the specific order of the fields I want to move between. This has the benefit of working across all subviews in the main view so that one can navigate between UITextField's and UITextView's (and any other control) as needed.
Generally - I typically try to have view controllers message between views and custom event handler objects. So I use a message (aka, NSNotification) passed back to the view controller from a custom delegate class.
(TextField Delegate Handler)
Note: In AppDelegate.swift: let defaultCenter = NSNotificationCenter.defaultCenter()
//Globally scoped
struct MNGTextFieldEvents {
static let NextButtonTappedForTextField = "MNGTextFieldHandler.NextButtonTappedForTextField"
}
class MNGTextFieldHandler: NSObject, UITextFieldDelegate {
var fields:[UITextField]? = []
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return true
}
func textFieldDidBeginEditing(textField: UITextField) {
textField.backgroundColor = UIColor.yellowColor()
}
func textFieldDidEndEditing(textField: UITextField) {
textField.backgroundColor = UIColor.whiteColor()
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return true
}
func textFieldShouldClear(textField: UITextField) -> Bool {
return false
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
return true
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
//passes the message and the textField (with tag) calling the method
defaultCenter.postNotification(NSNotification(name: MNGTextFieldEvents.NextButtonTappedForTextField, object: textField))
return false
}
}
This allows my view controller to remain focused on it's main job of handling the messaging between objects, model and view.
(View Controller receives a message from the delegate and passes instructions using the advanceToNextField function)
Note: In my storyboard my custom handler classes are defined using an NSObject and that object is linked into the storyboard as a delegate for the controls that I need monitored. Which causes the custom handler class to be initialized automatically.
class MyViewController: UIViewController {
#IBOutlet weak var tagsField: UITextField! { didSet {
(tagsField.delegate as? MNGTextFieldHandler)!.fields?.append(tagsField)
}
}
#IBOutlet weak var titleField: UITextField!{ didSet {
(titleField.delegate as? MNGTextFieldHandler)!.fields?.append(titleField)
}
}
#IBOutlet weak var textView: UITextView! { didSet {
(textView.delegate as? MNGTextViewHandler)!.fields?.append(textView)
}
}
private struct Constants {
static let SelectorAdvanceToNextField = Selector("advanceToNextField:")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerEventObservers()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
deRegisterEventObservers()
}
func advanceToNextField(notification:NSNotification) {
let currentTag = (notification.object as! UIView).tag
for aView in self.view.subviews {
if aView.tag == currentTag + 1 {
aView.becomeFirstResponder()
}
}
}
func registerEventObservers () {
defaultCenter.addObserver(self, selector: Constants.SelectorAdvanceToNextField, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil)
}
func deRegisterEventObservers() {
defaultCenter.removeObserver(self, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil)
}
....
}
Just another way to achieve the result that I found helpful. My app had 11 text fields followed by a text view. I needed to be able to cycle through all fields using the next key and then resign the keyboard following the textview (i.e. other notes).
In the storyboard, I set the tag on all of the fields (both text and textview) starting with 1 through 12, 12 being the textview.
I'm sure there are other ways to do it and this method isn't perfect, but hopefully it helps someone.
In code, I wrote the following:
func textFieldShouldReturn(textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
//Handle Textview transition, Textfield programmatically
if textField.tag == 11 {
//Current tag is 11, next field is a textview
self.OtherNotes.becomeFirstResponder()
} else if nextTag > 11 {
//12 is the end, close keyboard
textField.resignFirstResponder()
} else {
//Between 1 and 11 cycle through using next button
let nextResponder = self.view.viewWithTag(nextTag) as? UITextField
nextResponder?.becomeFirstResponder()
}
return false
}
func textFieldDidEndEditing(textField: UITextField) {
textField.resignFirstResponder()
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
//Remove keyboard when clicking Done on keyboard
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
Another approach, if you're using storyboards, you can change the textfield's attribute for Return Key.
Currently you have the following options: Default (Return), Go, Google, Join, Next, Route, Search, Send, Yahoo, Done, Emergency Call, Continue

Resources