Why does only one of my NSTimers work? - cocoa

I don't understand why only the animateDarkAst animation works. The first two timers (the one that operates on processKeys and the one that operates on animateDarkAst) work fine but the other timers don't. It doesn't matter what order I write the timers only those two methods work with their respective timers. For the other 3 animations, nothing appears on the screen because no code is being processed within their methods (animateLightAst, animateSmallAst, animateComet).
// Prepare asteroids.
_asteroiddark = [[NSImageView alloc] init];
[_asteroiddark setImage: [NSImage imageNamed:#"asteroiddark"]];
[_asteroiddark setFrame: theModel.darkAstRect];
_asteroidlight = [[NSImageView alloc] init];
[_asteroidlight setImage: [NSImage imageNamed:#"asteroidwhite"]];
[_asteroidlight setFrame: theModel.lightAstRect];
_asteroidsmall = [[NSImageView alloc] init];
[_asteroidsmall setImage: [NSImage imageNamed:#"asteroidsmall"]];
[_asteroidsmall setFrame: theModel.smallAstRect];
// ... and comets
_comet = [[NSImageView alloc] init];
[_comet setImage:[NSImage imageNamed:#"comet"]];
[_comet setFrame: theModel.cometRect];
// Set up key Processing timer for fluid spaceship movement.
timer1 = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(processKeys) userInfo:nil repeats:YES];
// Make a random value for first animation timer.
double randomInterval1 = arc4random() % (4 - 1) + 1;
// Set up key Processing timer for animation.
timer2 = [NSTimer scheduledTimerWithTimeInterval:randomInterval1 target:self selector:#selector(animateDarkAst) userInfo:nil repeats:YES];
double randomInterval2 = ((double)arc4random() / 3) * (3 - 1) + 1;
timer3 = [NSTimer scheduledTimerWithTimeInterval:randomInterval2 target:self selector:#selector(animateSmallAst) userInfo:nil repeats:YES];
double randomInterval3 = ((double)arc4random() / 5) * (5 - 1) + 1;
timer4 = [NSTimer scheduledTimerWithTimeInterval:randomInterval3 target:self selector:#selector(animateLightAst) userInfo:nil repeats:YES];
double randomInterval4 = ((double)arc4random() / 6) * (6 - 1) + 1;
timer5 = [NSTimer scheduledTimerWithTimeInterval:randomInterval4 target:self selector:#selector(animateComet) userInfo:nil repeats:YES];
}
return self;
}
-(void) animateDarkAst
{
int randX = arc4random_uniform(self.bounds.size.width);
int randSize = 40 + arc4random() % (120-40+1);
CGPoint startPoint = CGPointMake(randX, self.bounds.size.height);
[_asteroiddark setFrame: NSMakeRect(startPoint.x, startPoint.y, randSize, randSize)];
[self addSubview:_asteroiddark];
// Create animation (down y-axis)
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration:1.5];
[context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
_asteroiddark.animator.frame = CGRectOffset(_asteroiddark.frame, 0, -self.bounds.size.height - 180);
} completionHandler:nil];
}
-(void) animateSmallAst
{
int randX = arc4random_uniform(self.bounds.size.width);
int randSize = 40 + arc4random() % (120-40+1);
CGPoint startPoint = CGPointMake(randX, self.bounds.size.height);
[_asteroidsmall setFrame: NSMakeRect(startPoint.x, startPoint.y, randSize, randSize)];
[self addSubview:_asteroidsmall];
// Create animation (down y-axis)
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration:1.5];
[context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
_asteroidsmall.animator.frame = CGRectOffset(_asteroidsmall.frame, 0, -self.bounds.size.height - 180);
} completionHandler:nil];
}
-(void) animateLightAst
{
int randX = arc4random_uniform(self.bounds.size.width);
int randSize = 40 + arc4random() % (120-40+1);
CGPoint startPoint = CGPointMake(randX, self.bounds.size.height);
[_asteroidlight setFrame: NSMakeRect(startPoint.x, startPoint.y, randSize, randSize)];
[self addSubview:_asteroidlight];
// Create animation (down y-axis)
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration:1.5];
[context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
_asteroidlight.animator.frame = CGRectOffset(_asteroidlight.frame, 0, -self.bounds.size.height - 180);
} completionHandler:nil];
}
-(void) animateComet
{
int randX = arc4random_uniform(self.bounds.size.width);
int randSize = 40 + arc4random() % (120-40+1);
CGPoint startPoint = CGPointMake(randX, self.bounds.size.height);
[_comet setFrame: NSMakeRect(startPoint.x, startPoint.y, randSize, randSize)];
[self addSubview:_comet];
// Create animation (down y-axis)
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration:1.5];
[context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
_comet.animator.frame = CGRectOffset(_comet.frame, 0, -self.bounds.size.height - 180);
} completionHandler:nil];
}
How should I be handling this? Can I not have 5 timers at once?

The correct Q would be: "Why does at least one timer work?"
As you can see in the documentation the signature of the method has to take one parameter (the instance of NSTimer that send the message to the delegate).
aSelector The message to send to target when the timer fires.
The selector should have the following signature: timerFireMethod:
(including a colon to indicate that the method takes an argument). The
timer passes itself as the argument, thus the method would adopt the
following pattern:
Change the signature of the methods and of the selector when adding the timer.

Related

UIButton Randomize Title With Single Characters

The code below takes a string, adds each letter to an array and shuffles that array and shows the end result in a label. That works well. But I'd like for each character to contain a single character of the shuffled string. Right now it almost works, but it always repeats the characters. Like instead of having a series of 6 buttons with their titles: L e a g u e, the code generates repeated characters like: Leaauu.
My code is this:
- (IBAction)shuffleButttonTitles:(id)sender {
// The mutable array must be created here to create a new instance each time the button is tapped
letters = [[NSMutableArray alloc] init];
str = #"League";
length = str.length;
NSString *letter;
UIButton *button;
// First loop through the string and add each letter to an array
for (int i = 0; i < length; i++) {
letter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[letters addObject:letter];
}
// Shuffle the string for the label/buttons
for (int i = 0; i < length; i++) {
int value = arc4random() % (length - 1);
[letters exchangeObjectAtIndex:i withObjectAtIndex:value];
//Create the button and shuffle the letters for their titles
button = [[UIButton alloc] initWithFrame:CGRectMake(50 * i, 350, 44, 44)];
// HERE THE CODE REPEATS THE CHARACTERS
[button setTitle:[letters objectAtIndex:i] forState:UIControlStateNormal];
//Store the button in our array
[myButtons addObject:button];
NSLog(#"Letters in Array: %lu", letters.count);
}
for (UIButton *button in myButtons){
[button setBackgroundColor:[UIColor redColor]];
[self.view addSubview:button];
}
// Now we set the randomized title to the label
NSString *results = [letters componentsJoinedByString:#""];
string.text = results;
}
After some searching on the web I've figured it out. I post the complete code for others. This code takes a random string from the Characters.txt file and shuffles that string. Then it rotates the tiles slighty. You can enter the correct word when you have figured out the anagram, which then shows an alert view if you got it or didn't get it.
#define kTileSpacing 20
#define randomf(minX,maxX) ((float)(arc4random() % (maxX - minX + 1)) + (float)minX)
#interface ViewController ()
#end
#implementation ViewController
{
}
#synthesize progressView;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Anagrams" ofType:#"plist"];
dictionary = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSString *quotesFile = [[NSBundle mainBundle] pathForResource:#"Characters" ofType:#"txt"];
fileContents = [NSString stringWithContentsOfFile:quotesFile encoding:NSUTF8StringEncoding error:NULL];
// [txtField becomeFirstResponder];
//[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkWord:) name:nil object:nil];
}
- (IBAction)clear:(id)sender {
quoteArray = [fileContents componentsSeparatedByString:#"\n"];
NSString *quoteToDisplay;
currentQuestion = arc4random() % quoteArray.count;
quoteToDisplay = [quoteArray objectAtIndex: currentQuestion];
welldone.text = quoteToDisplay;
txtField.text = nil;
[txtField becomeFirstResponder];
for (UILabel *lbl in myButtons) {
[lbl removeFromSuperview];
}
}
- (IBAction)ShuffleString:(id)sender {
[self clear:nil];
// The mutable array must be created here to create a new instance each time the button is tapped
charactersArray = [[NSMutableArray alloc] init];
indexArray = [[NSMutableArray alloc] init];
myButtons = [[NSMutableArray alloc] init];
// 1. Shuffle the plist with the words to form anagrams from
currentQuestion = arc4random() % quoteArray.count;
str = [quoteArray objectAtIndex: currentQuestion]; //[[dictionary objectAtIndex:currentQuestion] objectForKey:#"Anagram"];
length = str.length;
NSString *letter;
// 2. Loop throught the chosen word and break it down into its letters and add them to an array
for (int i = 0; i < str.length; i++) {
// [charactersArray removeObjectAtIndex:i];
letter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[charactersArray addObject:letter];
// NSLog(#"Number of letters: %#", charactersArray);
}
while ([charactersArray count]) {
int randomizing = arc4random() % [charactersArray count];
[indexArray addObject:[charactersArray objectAtIndex:randomizing]];
[charactersArray removeObjectAtIndex:randomizing];
// NSLog(#"NO REPEAT SHUFFLE: %lu", (unsigned long)indexArray.count);
}
/***************/
CGFloat staticY = self.view.bounds.size.height / 9 * 1; // Static X for all buttons.
CGFloat staticWidth = 46; // Static Width for all Buttons.
CGFloat staticHeight = 46; // Static Height for all buttons.
CGFloat staticPadding = 10; // Padding to add between each button.
float tileSize = ceilf( self.view.bounds.size.width / str.length );
NSLog(#"size %f", tileSize);
CGFloat xOffset = (self.view.bounds.size.width - str.length * (44+staticPadding));
NSLog(#"xOffset %f", tileSize);
xOffset = tileSize/ 2;
for (int i = 0; i < str.length; i++) {
singleCharacterLabel = [[UILabel alloc] init];
singleCharacterLabel.textAlignment = NSTextAlignmentCenter;
singleCharacterLabel.font = [UIFont fontWithName:#"Verdana-Bold" size:21];
singleCharacterLabel.frame = CGRectMake((staticPadding + (i * (staticHeight + staticPadding))), staticY, staticWidth, staticHeight);
// NSLog(#"X: %f", (staticPadding + (i * (staticHeight + staticPadding))));
//singleCharacterLabel.center = CGPointMake(i * 50 + self.view.bounds.origin.x + self.view.bounds.size.width /3, 80); // i * int +self... int = space between labels. Here it is '50'
// singleCharacterLabel.center = CGPointMake(self.view.bounds.size.width * i, self.view.bounds.size.height / 5 * 1); // 1/4th down from the top
singleCharacterLabel.layer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Tile.png"]].CGColor;
NSString *anagramString = [indexArray objectAtIndex:i];
singleCharacterLabel.text = anagramString;
[myButtons addObject:singleCharacterLabel];
//1
//set random rotation of the tile
//anywhere between -0.2 and 0.3 radians
float rotation = randomf(0,50) / (float)100 - 0.2;
singleCharacterLabel.transform = CGAffineTransformMakeRotation( rotation );
//2
//move randomly upwards
int yOffset = (arc4random() % 10) - 10;
singleCharacterLabel.center = CGPointMake(singleCharacterLabel.center.x, singleCharacterLabel.center.y + yOffset);
[self.view addSubview:singleCharacterLabel];
//NSLog(#"LOOP: %#", anagramString);
}
}
- (IBAction)checkWord:(id)sender {
if (([txtField.text isEqual:str])) {
alertCorrect = [[UIAlertView alloc] initWithTitle:#"" message:#"Well done!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Next", nil];
[alertCorrect show];
} else {
alertWrong = [[UIAlertView alloc] initWithTitle:#"" message:#"Sorry, try again." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertWrong show];
}
// NSLog(#"String is: %lu", str.length);
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView == alertCorrect) {
if (buttonIndex == 0) {
[self ShuffleString:nil];
NSLog(#"next");
}
}
if (alertView == alertWrong) {
if (buttonIndex == 1) {
// Wrong answer. Close view and let user try again
}
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (txtField.text.length == length) {
[self checkWord:nil];
NSLog(#"You entered %lu characters", length);
}
return YES;
}

Terminate glView in cocos2d

I made an App using cocos2D that has a hierarchical structure.
And I used a UIButton on glView in a child layer.
When I get back to parent layer, the button still on it's position.
I want to terminate that.
How can I do this?
Here's the code.
MainHallLayer.m
- (id)init
{
self = [super init];
if (self != nil)
{
CCMenu *menuLists = [CCMenu menuWithItems: nil];
menuLists.position = ccp(screenSize.width/2, screenSize.height/2);
[self addChild:menuLists z:10];
NSString *fontName = [NSString stringWithFormat:#"Chalkboard SE"];
CGFloat fontSize = 28;
{
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Touch Field : Getting
touches coordinates" fontName:fontName fontSize:fontSize];
[label setColor:ccc3(0.0, 0.0, 0.0)];
label.anchorPoint = ccp(0, 0.5f);
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id
sender)
{
CCScene *scene = [TouchField1Layer node];
[ReturningNode returnToNodeWithParent:scene];
[[CCDirector sharedDirector] replaceScene:scene];
}];
[menuLists addChild:menuItem];
}
}
return self;
}
ReturnToNode.m
- (id)initWithParentNode:(CCNode *)parentNode
{
self = [super init];
if (self != nil)
{
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CCLabelTTF *label = [CCLabelTTF labelWithString:#" <= Return"
fontName:#"Gill Sans"
fontSize:30.0f];
label.color = ccMAGENTA;
id tint_1 = [CCTintTo actionWithDuration:0.333f red:1.0 green:.0f blue:.0f];
id tint_2 = [CCTintTo actionWithDuration:0.333f red:.5f green:.5f blue:.0f];
id tint_3 = [CCTintTo actionWithDuration:0.333f red:.0f green:1.0 blue:.0f];
id tint_4 = [CCTintTo actionWithDuration:0.333f red:.0f green:.5f blue:.5f];
id tint_5 = [CCTintTo actionWithDuration:0.333f red:.0f green:.0f blue:1.0];
id tint_6 = [CCTintTo actionWithDuration:0.333f red:.5f green:.0f blue:.5f];
id sequence = [CCSequence actions:tint_1,tint_2,tint_3,tint_4,tint_5,tint_6, nil];
id repeatAction = [CCRepeatForever actionWithAction:sequence];
[label runAction:repeatAction];
CCLayerColor *labelBackground = [CCLayerColor layerWithColor:ccc4(0.0, 0.0, 70, 40) width:label.contentSize.width + 20 height:label.contentSize.height + 20];
[label addChild:labelBackground z:-1];
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id sender)
{
[self removeFromParentAndCleanup:YES];
[[CCDirector sharedDirector] replaceScene:[TouchControllerLayer scene]];
}];
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
[menu alignItemsVertically];
menu.position = CGPointMake(label.contentSize.width/2 , screenSize.height - 20);
[self addChild:menu];
[parentNode addChild:self z:1000];
}
return self;
}
+ (id)returnToNodeWithParent:(CCNode *)parentNode
{
return [[[self alloc] initWithParentNode:parentNode] autorelease];
}
TouchField1Layer.m
- (id)init
{
self = [super init];
if (self != nil)
{
BackgroundLayer *background = [BackgroundLayer node];
TouchField3Layer *layer = [TouchField3Layer node];
[self addChild:background z:3];
[self addChild:layer z:2];
[self preparingTools];
}
return self;
}
- (void)preparingTools
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(0, 0, 100, 40);
UIView *glView = [CCDirector sharedDirector].openGLView;
glView.tag = TAG_GLVIEW;
[glView addSubview:button];
}
Any advices, helps are welcome. Always thanks. Bless You.
If you add UIKit views you will have to remove them manually at the appropriate point in time. Cocos2D does not manage the lifetime of UIKit views, only classes derived from CCNode.
For example when changing scenes you will have to remove the UIButton from the glView either in -(void) dealloc or in -(void) cleanup.

UITouch Event on Animated UIImagevIew

I am using the Snowfall sample code which drops UImages from the top of the screen. I want to detect a Touch on the UIImageview and update a label. If i create a single UIImageView in IBOutlet and connect it to the touch event the label is updated correctly. But when I try to apply it to the falling UIImageView it does not work. Here is what I have so far:
- (void)viewDidLoad {
[super viewDidLoad];
score = 0;
self.view.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0];
flakeImage = [UIImage imageNamed:#"flake.png"];
[NSTimer scheduledTimerWithTimeInterval:(0.5) target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
- (void)onTimer
{
flakeView = [[UIImageView alloc] initWithImage:flakeImage];
flakeView.opaque = YES;
flakeView.userInteractionEnabled = YES;
flakeView.multipleTouchEnabled = YES;
int startX = round(random() % 320);
int endX = round(random() % 320);
double scale = 1 / round(random() % 100) + 1.0;
double speed = 1 / round(random() % 100) + 1.0;
flakeView.frame = CGRectMake(startX, -100.0, 25.0 * scale, 25.0 * scale);
[self.view addSubview:flakeView];
[self.view bringSubviewToFront:flakeView];
[UIView beginAnimations:nil context:flakeView];
[UIView setAnimationDuration:5 * speed];
flakeView.frame = CGRectMake(endX, 500.0, 25.0 * scale, 25.0 * scale);
[UIView setAnimationDidStopSelector:#selector(onAnimationComplete:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == flakeView)
{
NSLog(#"tag %#",touch);
score = score + 1;
lbl1.text = [NSString stringWithFormat:#"%i", score];
}
}
Animations typically disable touch handling so you have to use:
animateWithDuration:delay:options:animations:completion
to set the options to receive touches.
http://developer.apple.com/library/IOs/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6-SW1
IIRC the option you want is: UIViewAnimationOptionAllowUserInteraction Look for UIViewAnimationOptions in the above doc for the full list.

XCODE - (iOS) Timing / synchronising a view behaving like a slide show to a video

This one has been doing my head in for months - So time to swallow my pride and reach out for a little help. At the moment this is being done in UIWebView as HTML5/JS controlled system. But UIWebview frankly sux and looking to make this last component native too.
I have a collection of videos and at specific timed points during the video, I am calling a page of instructions that relate to the timed period in the video. The video controls also act as a controller for the instructions pages. So whatever timed point is reached, the corresponding page is animated into place.
I've looked in many, many options, with the closest coming in with http video streaming and using timed metadata to initiate a view, but I am containing the videos locally on the device. And, as yet cannot find anything that looks like it will work. Seems simple enough in principle, but I'll be damned if I can find a decent solution...
Any ideas / pointers?
Here's the last attempt at going native with this before the remainder of my hair fell out - I think I may be seeing where I was heading in the wrong direction, but if you can spare a few moments, I'd really appreciate it!
OBJECTIVE is to have a shoutOut that lives below the video that contains a page of instructions. At x seconds, the content will be refreshed to correspond to that portion of the video and persist until the next shoutOut for fresh content. This I have managed to achieve successfully. Where I have been falling down (a lot) is when I scrub the video back to a previous section, the shoutOut content remains at the position from which I scrubbed and remains there permanently. Or as the code is below, simply doesn't re-apear as it is set to a timed visible duration.
Anyway, here's the code...
Header:
// START:import
#import <UIKit/UIKit.h>
// START_HIGHLIGHT
#import <MediaPlayer/MPMoviePlayerController.h>
#import "CommentView.h"
// END_HIGHLIGHT
// START:def
// START:wiring
#interface MoviePlayerViewController : UIViewController {
UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
MPMoviePlayerController *player;
// END_HIGHLIGHT
// START:wiring
UILabel *onScreenDisplayLabel;
UIScrollView *myScrollView;
NSMutableArray *keyframeTimes;
NSArray *shoutOutTexts;
NSArray *shoutOutTimes;
}
#property (nonatomic, retain) IBOutlet UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
#property (nonatomic, retain) MPMoviePlayerController *player;
// END_HIGHLIGHT
#property (nonatomic, retain) IBOutlet UILabel *onScreenDisplayLabel;
#property (nonatomic, retain) IBOutlet UIScrollView *myScrollView;
#property (nonatomic, retain) NSMutableArray *keyframeTimes;
// START_HIGHLIGHT
-(NSURL *)movieURL;
- (void)timerAction:(NSTimer*)theTimer;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification;
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer;
- (IBAction) getInfo:(id)sender;
- (void)removeView:(NSTimer*)theTimer;
// END_HIGHLIGHT
// START:wiring
#end
// END:def
// END:wiring
// END:import
Main:
#implementation MoviePlayerViewController
// START:synth
#synthesize player;
#synthesize viewForMovie;
#synthesize onScreenDisplayLabel;
#synthesize myScrollView;
#synthesize keyframeTimes;
// END:synth
// Implement loadView to create a view hierarchy programmatically, without using a nib.
// START:viewDidLoad
// START:viewDidLoad1
- (void)viewDidLoad {
[super viewDidLoad];
keyframeTimes = [[NSMutableArray alloc] init];
shoutOutTexts = [[NSArray
arrayWithObjects:#"This is a test\nLabel at 2 secs ",
#"This is a test\nLabel at 325 secs",
nil] retain];
shoutOutTimes = [[NSArray
arrayWithObjects:[[NSNumber alloc] initWithInt: 2],
[[NSNumber alloc] initWithInt: 325],
nil] retain];
self.player = [[MPMoviePlayerController alloc] init];
self.player.contentURL = [self movieURL];
// END:viewDidLoad1
self.player.view.frame = self.viewForMovie.bounds;
self.player.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.viewForMovie addSubview:player.view];
[self.player play];
// START_HIGHLIGHT
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
// END_HIGHLIGHT
// START:viewDidLoad1
[self.view addSubview:self.myScrollView];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:nil];
}
// END:viewDidLoad
// END:viewDidLoad1
// START:movieURL
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath =
[bundle
pathForResource:#"BigBuckBunny_640x360"
ofType:#"m4v"];
if (moviePath) {
return [NSURL fileURLWithPath:moviePath];
} else {
return nil;
}
}
// END:movieURL
int position = 0;
- (void)timerAction:(NSTimer*)theTimer {
NSLog(#"hi");
int count = [shoutOutTimes count];
NSLog(#"count is at %d", count);
if (position < count) {
NSNumber *timeObj = [shoutOutTimes objectAtIndex:position];
int time = [timeObj intValue];
NSLog(#"time is at %d", time);
if (self.player.currentPlaybackTime >= time) {
CommentView *cview = [[CommentView alloc]
initWithText:[shoutOutTexts objectAtIndex:position]];
[self.player.view addSubview:cview];
position++;
[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:#selector(removeView:) userInfo:cview repeats:NO];
}
}
}
- (void)removeView:(NSTimer*)theTimer {
UIView *view = [theTimer userInfo];
[view removeFromSuperview];
}
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
- (void) movieDurationAvailable:(NSNotification*)notification {
MPMoviePlayerController *moviePlayer = [notification object];
int duration = [moviePlayer duration];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playerThumbnailImageRequestDidFinish:)
name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
object:nil];
NSMutableArray *times = [[NSMutableArray alloc] init];
for(int i = 0; i < 20; i++) {
[times addObject:[NSNumber numberWithInt:5+i*((duration)/20)]];
}
[self.player requestThumbnailImagesAtTimes:times timeOption: MPMovieTimeOptionNearestKeyFrame];
}
int p = 0;
int ll=0;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification {
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *timecode;
timecode = [userInfo objectForKey: #"MPMoviePlayerThumbnailTimeKey"];
[keyframeTimes addObject: timecode];
UIImage *image;
image = [userInfo objectForKey: #"MPMoviePlayerThumbnailImageKey"];
int width = image.size.width;
int height = image.size.height;
float newwidth = 75 * ((float)width / (float)height);
self.myScrollView.contentSize = CGSizeMake((newwidth + 2) * 20, 75);
UIImageView *imgv = [[UIImageView alloc] initWithImage:image];
[imgv setUserInteractionEnabled:YES];
[imgv setFrame:CGRectMake(ll, 0, newwidth, 75.0f)];
ll+=newwidth + 2;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapFrom:)];
[tapRecognizer setNumberOfTapsRequired:1];
[imgv addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
[myScrollView addSubview:imgv];
}
- (void) getInfo:(id)sender
{
MPMovieMediaTypeMask mask = self.player.movieMediaTypes;
NSMutableString *mediaTypes = [[NSMutableString alloc] init];
if (mask == MPMovieMediaTypeMaskNone) {
[mediaTypes appendString:#"Unknown Media Type"];
} else {
if (mask & MPMovieMediaTypeMaskAudio) {
[mediaTypes appendString:#"Audio"];
}
if (mask & MPMovieMediaTypeMaskVideo) {
[mediaTypes appendString:#"Video"];
}
}
MPMovieSourceType type = self.player.movieSourceType;
NSMutableString *sourceType = [[NSMutableString alloc] initWithString:#""];
if (type == MPMovieSourceTypeUnknown) {
[sourceType appendString:#"Source Unknown"];
} else if (type == MPMovieSourceTypeFile) {
[sourceType appendString:#"File"];
} else if (type == MPMovieSourceTypeStreaming) {
[sourceType appendString:#"Streaming"];
}
CGSize size = self.player.naturalSize;
onScreenDisplayLabel.text = [NSString stringWithFormat:#"[Type: %#] [Source: %#] [Time: %.1f of %.f secs] [Playback: %.0fx] [Size: %.0fx%.0f]",
mediaTypes,
sourceType,
self.player.currentPlaybackTime,
self.player.duration,
self.player.currentPlaybackRate,
size.width,
size.height];
}
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSArray *subviews = [myScrollView subviews];
for (int i = 0; i < 20; i++) {
if (recognizer.view == [subviews objectAtIndex:i]) {
NSNumber *num = [keyframeTimes objectAtIndex:i];
self.player.currentPlaybackTime = [num intValue];
return;
}
}
}
#end
The Comment View Header:
#import <UIKit/UIKit.h>
#interface CommentView : UIView {
}
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text;
- (id)initWithText:(NSString *) text;
#end
The Comment View Main:
#import "CommentView.h"
#implementation CommentView
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text {
if ((self = [super initWithFrame:frame])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (id)initWithText:(NSString *) text {
if ((self = [super init])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
Thoughts anyone?
Cheers!
What's wrong with monitoring currentPlaybackTime at regular intervals (assuming you are using an instance that implements MPMediaPlayback for playback).

Get UITableView to scroll to the selected UITextField and Avoid Being Hidden by Keyboard

I have a UITextField in a table view on a UIViewController (not a UITableViewController).
If the table view is on a UITableViewController, the table will automatically scroll to the textField being edited to prevent it from being hidden by the keyboard. But on a UIViewController it does not.
I have tried for a couple of days reading through multiple ways to try to accomplish this and I cannot get it to work. The closest thing that actually scrolls is:
-(void) textFieldDidBeginEditing:(UITextField *)textField {
// SUPPOSEDLY Scroll to the current text field
CGRect textFieldRect = [textField frame];
[self.wordsTableView scrollRectToVisible:textFieldRect animated:YES];
}
However this only scrolls the table to the topmost row.
What seems like an easy task has been a couple of days of frustration.
I am using the following to construct the tableView cells:
- (UITableViewCell *)tableView:(UITableView *)aTableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [NSString stringWithFormat: #"%d:%d", [indexPath indexAtPosition: 0], [indexPath indexAtPosition:1]];
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
UITextField *theTextField = [[UITextField alloc] initWithFrame:CGRectMake(180, 10, 130, 25)];
theTextField.adjustsFontSizeToFitWidth = YES;
theTextField.textColor = [UIColor redColor];
theTextField.text = [textFieldArray objectAtIndex:indexPath.row];
theTextField.keyboardType = UIKeyboardTypeDefault;
theTextField.returnKeyType = UIReturnKeyDone;
theTextField.font = [UIFont boldSystemFontOfSize:14];
theTextField.backgroundColor = [UIColor whiteColor];
theTextField.autocorrectionType = UITextAutocorrectionTypeNo;
theTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
theTextField.clearsOnBeginEditing = NO;
theTextField.textAlignment = UITextAlignmentLeft;
//theTextField.tag = 0;
theTextField.tag=indexPath.row;
theTextField.delegate = self;
theTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
[theTextField setEnabled: YES];
[cell addSubview:theTextField];
[theTextField release];
}
return cell;
}
I suspect I can get the tableView to scroll properly if I can somehow pass the indexPath.row in the textFieldDidBeginEditing method?
Any help is appreciated.
In my app, I have successfully used a combination of contentInset and scrollToRowAtIndexPath like this:
When you want to display the keyboard, just add a contentInset at the bottom with your table with desired height:
tableView.contentInset = UIEdgeInsetsMake(0, 0, height, 0);
Then, you can safely use
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:cell_index inSection:cell_section] animated:YES];
By adding the contentInset, even if you are focusing on the last cell the tableView will still be able to scroll. Just make sure that when you are dismissing the keyboard, you reset the contentInset.
EDIT:
If you have only one section (you can replace cell_section with 0) and the use the textView tag to inform the cell row.
Swift
#objc private func keyboardWillShow(_ notification: Notification) {
guard let userinfo = notification.userInfo else {
return
}
guard
let duration = (userinfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue,
let endFrame = (userinfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let curveOption = userinfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
UIView.animate(withDuration: duration, delay: 0, options: [.beginFromCurrentState, .init(rawValue: curveOption)], animations: {
let edgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: endFrame.height, right: 0)
self.scrollView.contentInset = edgeInsets
self.scrollView.scrollIndicatorInsets = edgeInsets
})
}
#objc private func keyboardWillHide(_ notification: Notification) {
guard let userinfo = notification.userInfo else {
return
}
guard
let duration = (userinfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue,
let curveOption = userinfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
UIView.animate(withDuration: duration, delay: 0, options: [.beginFromCurrentState, .init(rawValue: curveOption)], animations: {
let edgeInsets = UIEdgeInsets.zero
self.scrollView.contentInset = edgeInsets
self.scrollView.scrollIndicatorInsets = edgeInsets
})
}
override func viewDidLoad() {
super.viewDidLoad()
// ...
subscribeToKeyboardNotifications()
}
deinit {
unsubscribeFromKeyboardNotifications()
}
private func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIWindow.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIWindow.keyboardWillHideNotification, object: nil)
}
private func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: UIWindow.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIWindow.keyboardWillHideNotification, object: nil)
}
Objective C
- (void)keyboardWillShow:(NSNotification *)sender
{
CGFloat height = [[sender.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
NSTimeInterval duration = [[sender.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions curveOption = [[sender.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] << 16;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState|curveOption animations:^{
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 0, height, 0);
tableView.contentInset = edgeInsets;
tableView.scrollIndicatorInsets = edgeInsets;
} completion:nil];
}
- (void)keyboardWillHide:(NSNotification *)sender
{
NSTimeInterval duration = [[sender.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions curveOption = [[sender.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] << 16;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState|curveOption animations:^{
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
tableView.contentInset = edgeInsets;
tableView.scrollIndicatorInsets = edgeInsets;
} completion:nil];
}
And in - (void)viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
Then
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
This is a tweak to FunkyKat's answer (big thank you FunkyKat!). It would probably be beneficial to not hardcode UIEdgeInsetsZero for future iOS compatibility.
Instead, I ask for the current inset value and tweak the bottom value as needed.
- (void)keyboardWillShow:(NSNotification *)sender {
CGSize kbSize = [[[sender userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
NSTimeInterval duration = [[[sender userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGFloat height = UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]) ? kbSize.height : kbSize.width;
if (isIOS8()) height = kbSize.height;
[UIView animateWithDuration:duration animations:^{
UIEdgeInsets edgeInsets = [[self tableView] contentInset];
edgeInsets.bottom = height;
[[self tableView] setContentInset:edgeInsets];
edgeInsets = [[self tableView] scrollIndicatorInsets];
edgeInsets.bottom = height;
[[self tableView] setScrollIndicatorInsets:edgeInsets];
}];
}
- (void)keyboardWillHide:(NSNotification *)sender {
NSTimeInterval duration = [[[sender userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^{
UIEdgeInsets edgeInsets = [[self tableView] contentInset];
edgeInsets.bottom = 0;
[[self tableView] setContentInset:edgeInsets];
edgeInsets = [[self tableView] scrollIndicatorInsets];
edgeInsets.bottom = 0;
[[self tableView] setScrollIndicatorInsets:edgeInsets];
}];
}
For the sake of anyone else running into this issue, I'm posting the necessary methods here:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [NSString stringWithFormat: #"%d:%d", [indexPath indexAtPosition: 0], [indexPath indexAtPosition:1]];
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
UITextField *theTextField = [[UITextField alloc] initWithFrame:CGRectMake(180, 10, 130, 25)];
theTextField.keyboardType = UIKeyboardTypeDefault;
theTextField.returnKeyType = UIReturnKeyDone;
theTextField.clearsOnBeginEditing = NO;
theTextField.textAlignment = UITextAlignmentLeft;
// (The tag by indexPath.row is the critical part to identifying the appropriate
// row in textFieldDidBeginEditing and textFieldShouldEndEditing below:)
theTextField.tag=indexPath.row;
theTextField.delegate = self;
theTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
[theTextField setEnabled: YES];
[cell addSubview:theTextField];
[theTextField release];
}
return cell;
}
-(void) textFieldDidBeginEditing:(UITextField *)textField {
int z = textField.tag;
if (z > 4) {
// Only deal with the table row if the row index is 5
// or greater since the first five rows are already
// visible above the keyboard
// resize the UITableView to fit above the keyboard
self.wordsTableView.frame = CGRectMake(0.0,44.0,320.0,200.0);
// adjust the contentInset
wordsTableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 10);
// Scroll to the current text field
[wordsTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:z inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
// Determine which row is being edited
int z = textField.tag;
if (z > 4) {
// resize the UITableView to the original size
self.wordsTableView.frame = CGRectMake(0.0,44.0,320.0,416.0);
// Undo the contentInset
wordsTableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// Dismisses the keyboard when the "Done" button is clicked
[textField resignFirstResponder];
return YES;
}
I needed a simple solution so for me helped:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
let pointInTable = textField.superview!.convert(textField.frame.origin, to: tableView)
var tableVContentOffset = tableView.contentOffset
tableVContentOffset.y = pointInTable.y
if let accessoryView = textField.inputAccessoryView {
tableVContentOffset.y -= accessoryView.frame.size.height
}
tableView.setContentOffset(tableVContentOffset, animated: true)
return true;
}
Try my coding, this will help for ypu
tabelview.contentInset = UIEdgeInsetsMake(0, 0, 210, 0);
[tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:your_indexnumber inSection:Your_section]
atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
Apple has an official post explaining how to do this naturally as they do in the UITableViewController. My Stackoverflow answer has this explained along with a swift version.
https://stackoverflow.com/a/31869898/1032179
You need to resize the tableView itself so that it does not go under the keyboard.
-(void) textFieldDidBeginEditing:(UITextField *)textField {
// SUPPOSEDLY Scroll to the current text field
self.worldsTableView.frame = CGRectMake(//make the tableView smaller; to only be in the area above the keyboard);
CGRect textFieldRect = [textField frame];
[self.wordsTableView scrollRectToVisible:textFieldRect animated:YES];
}
Alternatively, you can use a keyboard notification; this works slightly better because you have more information, and is more consistent in terms of knowing when the keyboard is coming up:
//ViewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
And then implement:
- (void)keyboardWillShow:(NSNotification *)notification {
}
- (void)keyboardWillHide:(NSNotification *)notification {
}
My code. Maybe someone will be useful:
Custom textField cell in tableView
.m
#property (nonatomic, strong) UITextField *currentCellTextField;
CustomCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if (cell == nil) {
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
cell.textfield.delegate = self;
}
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
self.currentCellTextField = textField;
CGPoint pnt = [self.organisationTableView convertPoint:textField.bounds.origin fromView:textField];
NSIndexPath* path = [self.organisationTableView indexPathForRowAtPoint:pnt];
if (path.section >= 2) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
self.organisationTableView.contentInset = UIEdgeInsetsMake(0, 0, kOFFSET_FOR_KEYBOARD, 0);
CGPoint siize = self.organisationTableView.contentOffset;
siize.y =(pnt.y-170);
self.organisationTableView.contentOffset = CGPointMake(0, siize.y);
[UIView commitAnimations];
}
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
CGPoint pnt = [self.organisationTableView convertPoint:textField.bounds.origin fromView:textField];
NSIndexPath* path = [self.organisationTableView indexPathForRowAtPoint:pnt];
if (path.section >= 2) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
self.organisationTableView.contentInset = UIEdgeInsetsZero;
self.organisationTableView.contentOffset = CGPointMake(0, self.organisationTableView.contentOffset.y);
[UIView commitAnimations];
}
return YES;
}
In my case my UITableview was inside another UIView and that UIvie was in the main UIScrollview. So I used more general solution for those kind of problems.
I simply found the Y coordinate of my cell in the specific UIScrollView and then scrolled to correct point:
-(void)textFieldDidBeginEditing:(UITextField *)textField{
float kbHeight = 216;//Hard Coded and will not support lanscape mode
UITableViewCell *cell = (UITableViewCell *)[textField superview];
float scrollToHeight = [self FindCordinate:cell];
[(UIScrollView *)self.view setContentOffset:CGPointMake(0, scrollToHeight - kbHeight + cell.frame.size.height) animated:YES];
}
-(float)FindCordinate:(UIView *)cell{
float Ycordinate = 0.0;
while ([cell superview] != self.view) {
Ycordinate += cell.frame.origin.y;
cell = [cell superview];
}
Ycordinate += cell.frame.origin.y;
return Ycordinate;
}
Another simple solution is adding an additional space for the footer of the last table section:
- (float)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if (section == lastSection) {
return keyboard height;
}
return 0;
}
We can add our icon into this area as well. :)
You can try adding a UITableViewController to the UIViewController instead of just a table view. This way you can call UITableViewController's viewWillAppear and everything will appear to work.
Example:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[tableViewController viewWillAppear:animated];
}
I have add a little feature to #FunkyKat and #bmauter answers (great answer by the way, it should be the one accepted)
The regular Table View edge insets is preserved, before/after the keyboard apparition.
- (void)keyboardWillShow:(NSNotification *)sender
{
CGSize kbSize = [[[sender userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
NSTimeInterval duration = [[[sender userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGFloat height = UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]) ? kbSize.width : kbSize.height;
[UIView animateWithDuration:duration animations:^{
UIEdgeInsets edgeInsets = self.tableView.contentInset;
edgeInsets.bottom += height;
self.tableView.contentInset = edgeInsets;
edgeInsets = self.tableView.scrollIndicatorInsets;
edgeInsets.bottom += height;
self.tableView.scrollIndicatorInsets = edgeInsets;
}];
}
- (void)keyboardWillHide:(NSNotification *)sender
{
CGSize kbSize = [[[sender userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
NSTimeInterval duration = [[[sender userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGFloat height = UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]) ? kbSize.width : kbSize.height;
[UIView animateWithDuration:duration animations:^{
UIEdgeInsets edgeInsets = self.tableView.contentInset;
edgeInsets.bottom -= height;
self.tableView.contentInset = edgeInsets;
edgeInsets = self.tableView.scrollIndicatorInsets;
edgeInsets.bottom -= height;
self.tableView.scrollIndicatorInsets = edgeInsets;
}];
}

Resources