.xib file init UIView on ViewController User interaction not working - ios

I have a Storyboard with a ViewController using a navigation controller and a TabBarController. I have been trying to resolve this for a few days now.
The problem:
Very often user I cannot interact with the view... that includes scrolling, tapping the text field and any buttons being pressed. Something is stopping me from interacting with the newly loaded UIView.
I have tried using the init method and tried using the init with frame method. The view is showing all the time though so maybe this is not the problem.
I have tried remaking the whole xib file, re-coding the .h and .m files and re-attaching all the outlets on the view.
I am stuck
In the view controller I am loading a UIView with separate .xib file and separate .h and .m file.
This is how I am doing it:
in my ViewController in the viewDidAppear method:
int startPos = self.navigationController.navigationBar.frame.size.height+20;
inviteFriendsView = [[InviteFriendsEmailAddressesView alloc] init];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"InviteFriendsEmailAddressesView" owner:self options:nil];
inviteFriendsView = (InviteFriendsEmailAddressesView*)[nib objectAtIndex:0];
[self.view addSubview:inviteFriendsView];
[inviteFriendsView setDelegate:self];
[inviteFriendsView setUserInteractionEnabled:YES];
[inviteFriendsView customizeView];
[inviteFriendsView setAlpha:0.0];
[inviteFriendsView setY:startPos];
IBAction method for showing the view:
- (IBAction)inviteFriendsButtonTapped:(id)sender {
[self.view bringSubviewToFront:inviteFriendsView];
[inviteFriendsView setUserInteractionEnabled:YES];
[inviteFriendsView animate];
}
Here is the .h and .m files which show how I am loading the view:
InviteFriendsEmailAddressesView.h
#import <UIKit/UIKit.h>
#import "InviteFriendsViewDelegate.h"
#import "InviteFriendsNetworkContollerDelegate.h"
#import "InviteFriendsNetworkController.h"
#interface InviteFriendsEmailAddressesView : UIView <UITextFieldDelegate, UITextViewDelegate, InviteFriendsNetworkContollerDelegate, UIGestureRecognizerDelegate>
- (void) customizeView;
- (void) animate;
#property BOOL visible;
#property int y;
#property id<InviteFriendsViewDelegate> delegate;
#property (strong, nonatomic) NSArray *emails;
#property int height;
#property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
#property (weak, nonatomic) IBOutlet UILabel *inviteFriendsTitle;
#property (weak, nonatomic) IBOutlet UILabel *inviteFriendsDescription;
#property (weak, nonatomic) IBOutlet UITextField *userEmailTextField;
#property (weak, nonatomic) IBOutlet UIImageView *userEmailImageView;
#property (weak, nonatomic) IBOutlet UIButton *addFriendsButtonOutlet;
#property (weak, nonatomic) IBOutlet UILabel *emailAddressesDescription;
#property (weak, nonatomic) IBOutlet UIImageView *viewBackground;
#property (weak, nonatomic) IBOutlet UIView *emailAddressesCellBackground;
#property (weak, nonatomic) IBOutlet UILabel *emailAddressCellTextUILabel;
#property (weak, nonatomic) IBOutlet UIButton *emailAddressCancelButton;
#property (weak, nonatomic) IBOutlet UIView *emailAddressView;
#property (weak, nonatomic) IBOutlet UIButton *sendInviteButtonOutlet;
#property (weak, nonatomic) IBOutlet UIButton *progressSoFarButtonOutlet;
#property (weak, nonatomic) IBOutlet UIScrollView *viewScrollView;
#property (strong, nonatomic) InviteFriendsNetworkController *inviteFriendsNetworkController;
#pragma mark - UITextFieldDelegate Methods
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField; // return NO to disallow editing.
- (void)textFieldDidBeginEditing:(UITextField *)textField; // became first responder
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField; // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
- (void)textFieldDidEndEditing:(UITextField *)textField; // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; // return NO to not change text
- (BOOL)textFieldShouldClear:(UITextField *)textField; // called when clear button pressed. return NO to ignore (no notifications)
- (BOOL)textFieldShouldReturn:(UITextField *)textField; // called when 'return' key pressed. return NO to ignore.
#end
InviteFriendsEmailAddressesView.m
#import "InviteFriendsEmailAddressesView.h"
#import "UIFont+Theme.h"
#import "UIColor+Theme.h"
#import "UIImage+Theme.h"
#implementation InviteFriendsEmailAddressesView
#synthesize emails;
#synthesize visible;
#synthesize y;
#synthesize delegate;
#synthesize height;
#synthesize inviteFriendsNetworkController;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"InviteFriendsEmailAddressesView" owner:self options:nil];
self = [nib objectAtIndex:0];
}
return self;
}
- (id) init {
// self = [[[NSBundle mainBundle] loadNibNamed:#"InviteFriendsEmailAddressesView" owner:self options:nil] objectAtIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(themeChanged)
name:#"New Theme Applied"
object:nil];
NSUserDefaults *properties = [NSUserDefaults standardUserDefaults];
if([properties objectForKey:#"emails"]){
emails = [properties objectForKey:#"emails"];
} else {
emails = [[NSArray alloc] init];
}
[self.viewScrollView setDelegate:self];
[self.activityIndicator setHidden:YES];
[self.viewScrollView setScrollEnabled:YES];
//CGRect newScrollViewFrame = self.viewScrollView.frame;
//newScrollViewFrame.origin.y = 0;
//newScrollViewFrame.size.height = keyWindowFrame.size.height;
//[self.viewScrollView setFrame:newScrollViewFrame];
// get the size of the screen and set the content size to the size of the screen plus the bottom bar.
CGRect screenRect = [[UIScreen mainScreen] bounds];
screenRect.size.height = screenRect.size.height-200;
[self.viewScrollView setContentSize:CGSizeMake(screenRect.size.width, 1200)];
[self.viewScrollView setFrame:screenRect];
[_viewScrollView setScrollEnabled:YES];
// 100 is the size of the tool bar.
[self updateEmailListView];
self.userEmailTextField.delegate = self;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[self.viewScrollView addGestureRecognizer:singleTap];
//reactionNetworkController
inviteFriendsNetworkController = [[InviteFriendsNetworkController alloc] init];
[inviteFriendsNetworkController setDelegate:self];
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
- (void) updateEmailListView {
// With some valid UIView *view:
for(UIView *subview in [self.emailAddressView subviews]) {
if([subview isHidden] == NO){
[subview removeFromSuperview];
}
}
// if we have email addresses in the email address list.
if([emails count] > 0){
// display and populate scrollview with email addresses.
for(int i =0; i < [emails count]; i++){
// every other view set it white so that it creates a grey, white, grey, white pattern.
if(i % 2 == 0){
[self.emailAddressesCellBackground setBackgroundColor:[UIColor whiteColor]];
} else {
[self.emailAddressesCellBackground setBackgroundColor:[UIColor grayColor]];
}
// generate our background
UIView *newBackground = [[UIView alloc] initWithFrame:CGRectMake(
self.emailAddressesCellBackground.frame.origin.x,
self.emailAddressesCellBackground.frame.origin.y+self.emailAddressesCellBackground.frame.size.height*i,
self.emailAddressesCellBackground.frame.size.width,
self.emailAddressesCellBackground.frame.size.height)];
[newBackground setTag:i];
// generate our email addresses label.
UILabel *newLabel = [[UILabel alloc] initWithFrame:CGRectMake(
self.emailAddressCellTextUILabel.frame.origin.x,
self.emailAddressCellTextUILabel.frame.origin.y+self.emailAddressesCellBackground.frame.size.height*i,
self.emailAddressCellTextUILabel.frame.size.width,
self.emailAddressCellTextUILabel.frame.size.height)];
[newLabel setTag:i];
[newLabel setText:[emails objectAtIndex:i]];
// generate the delete button and add a target for the selector when it is pressed.
UIButton *newButton = [[UIButton alloc] initWithFrame:CGRectMake(
self.emailAddressCancelButton.frame.origin.x,
self.emailAddressCancelButton.frame.origin.y+self.emailAddressCancelButton.frame.size.height*i,
self.emailAddressCancelButton.frame.size.width,
self.emailAddressCancelButton.frame.size.height)];
[newButton setTag:i];
[newButton addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchDown];
[newButton setImage:self.emailAddressCancelButton.imageView.image forState:UIControlStateNormal];
// attach the new views to the scrollview
[self.emailAddressView addSubview:newBackground];
[self.emailAddressView addSubview:newButton];
[self.emailAddressView addSubview:newLabel];
}
[self.emailAddressView setHidden:NO];
}
// if we do not have any emails added yet.
else {
[self.emailAddressView setHidden:YES];
}
}
- (void) deleteButtonPressed:(id)sender{
int tag = [sender tag];
NSLog(#"delete button pressed with sender tag: %i", [sender tag]);
NSMutableArray *mutableEmails = [emails mutableCopy];
[mutableEmails removeObjectAtIndex:tag];
emails = mutableEmails;
NSUserDefaults *properties = [NSUserDefaults standardUserDefaults];
[properties setObject:emails forKey:#"emails"];
[properties synchronize];
[self updateEmailListView];
}
- (void) customizeView{
[self.inviteFriendsTitle setFont:[UIFont themeFontNamed:#"viewTitleFont" ofSize:18]];
[self.viewBackground setImage:[UIImage themeImageNamed:#"backgroundImage"]];
[self.inviteFriendsDescription setFont:[UIFont themeFontNamed:#"normalTextFont" ofSize:13]];
[self.emailAddressesDescription setFont:[UIFont themeFontNamed:#"normalTextFont" ofSize:13]];
if(height == 0 &&[delegate respondsToSelector:#selector(getHeight)]){
height = [delegate getHeight];
[self setFrame:CGRectMake(0,
-height,
self.frame.size.width,
height)];
}
}
- (void) animate{
if(visible == YES){
[self slideOut];
visible = NO;
}
else{
[self customizeView];
[self slideIn];
visible = YES;
}
NSLog(#"is user interaction enabled in InviteFriendsView?: %hhd", self.isUserInteractionEnabled);
}
- (void) themeChanged {
[self customizeView];
}
- (void) slideIn {
NSLog(#"Slide in");
[[self superview] setUserInteractionEnabled:NO];
[self.activityIndicator setAlpha:1.0];
[self.activityIndicator startAnimating];
self.alpha = 1.0;
[UIView animateWithDuration:0.5
animations:^{
[self setFrame:CGRectMake(0,
y,
self.frame.size.width,
height)];
}
completion:^(BOOL finished) {
NSLog(#"DID finish slide in");
}];
}
- (void) slideOut{
NSLog(#"Slide out");
if([delegate respondsToSelector:#selector(getY)]){
y = [delegate getY];
}
[self setViewScrollView:self.viewScrollView];
[self setUserInteractionEnabled:YES];
[self.viewScrollView setUserInteractionEnabled:YES];
[self.viewScrollView setScrollEnabled:YES];
[self.viewScrollView setScrollsToTop:YES];
[UIView animateWithDuration:0.5
animations:^{
[self setFrame:CGRectMake(0,
-height+y,
self.frame.size.width,
height)];
}
completion:^(BOOL finished) {
self.alpha = 0.0;
[[self superview] setUserInteractionEnabled:YES];
}];
}
#pragma mark - UITextFieldDelegate Methods
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
return true;
}
// return NO to disallow editing.
- (void)textFieldDidBeginEditing:(UITextField *)textField{
}
// became first responder
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
return true;
}
// return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
- (void)textFieldDidEndEditing:(UITextField *)textField{
}
// may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
return true;
}
// return NO to not change text
- (BOOL)textFieldShouldClear:(UITextField *)textField{
return true;
}
// called when clear button pressed. return NO to ignore (no notifications)
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[self addFriendToInviteButtonPressed:self];
[textField setText:#""];
return true;
}
// called when 'return' key pressed. return NO to ignore.
#pragma mark - Button Action Pressed Methods
- (IBAction)addFriendToInviteButtonPressed:(id)sender {
NSMutableArray *mutableEmails = [emails mutableCopy];
[mutableEmails addObject:self.userEmailTextField.text];
NSLog(#"mutableEmails: %#", mutableEmails);
NSUserDefaults *properties = [NSUserDefaults standardUserDefaults];
[properties setObject:mutableEmails forKey:#"emails"];
[properties synchronize];
emails = mutableEmails;
[self updateEmailListView];
[self.userEmailTextField setText:#""];
[self.userEmailTextField resignFirstResponder];
}
- (IBAction)sendInviteButtonPressed:(id)sender {
// send a POST request to the server with the emails.
NSString *stringEmails = [[emails valueForKey:#"description"] componentsJoinedByString:#""];
NSLog(#"stringEmails: %#", stringEmails);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int site = 0;
if([[defaults objectForKey:#"Theme"] isEqualToString:#"BOMG"]){
site = 1;
}
[self.inviteFriendsNetworkController inviteFriendsWithAddressList:emails AndSite:site];
}
- (IBAction)progressButtonPressed:(id)sender {
}
#pragma mark - InviteFriendsNetworkControllerDelegateMethods
- (void) didSendAddressList:(NSDictionary *)response{
}
- (void) failedTosendAddressList{
}
#pragma mark - UIGestureRecognizerDelegate methods
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
[self.userEmailTextField resignFirstResponder];
}
#end

This might be an issue with the frames, where the controls are rendered (partially) outside the superview frame. This can be caused by invalid autoresizing masks or autolayout constraints.
To debug this you can set 'clip subviews' to YES for the relavant controls. When you see some of the controls are not visible anymore (or just partially), check the frames and resizingmasks or constraints of the superview.

Check whether the view controller is added in the build phase compile source. If you not added the view controller in the compiler source (if its not added automatically) it will some effect in xib integration.

I found the answer. I had to comment out the line
[[self superview] setUserInteractionEnabled:NO];
inside slideIn(){..}
So disabling user interaction for the superview would of course stop all user interaction from happening in a subview (such as the scrollview).
Oh dear.
Thanks guys!

Related

When using JSQMessagesViewController as detailView in UISplitviewCOntroller, KeyBoardToolBar needs to appear in DetailViewController only

When using JSQMessagesViewController as detailView in UISplitViewController, KeyBoardToolBar needs to appear in DetailViewController only
late answer...
if u want to reduce the inputToolbar you need to create a subclass of JSQMessagesToolbarContentView and provide your own view for the tool bar's content view.
below i am giving the sample example, create a subcalss of JSQMessagesToolbarContentView name it as JSQMessagesToolbarContentView_custom in the subcalss add below code,
#import "JSQMessagesToolbarContentView.h"
#interface JSQMessagesToolbarContentView_custom : JSQMessagesToolbarContentView
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *holderViewLeadingConstraint;
#end
and in JSQMessagesToolbarContentView_custom.m file,
#import "UIView+JSQMessages.h"
#import "JSQMessagesToolbarContentView_custom.h"
#implementation JSQMessagesToolbarContentView_custom
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([JSQMessagesToolbarContentView_custom class])
bundle:[NSBundle bundleForClass:[JSQMessagesToolbarContentView_custom class]]];
}
#pragma mark - Initialization
- (void)awakeFromNib
{
[super awakeFromNib];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
self.backgroundColor = [UIColor clearColor];
}
//below method will place the contentview to desired position
- (void)layoutSubviews
{
[super layoutSubviews];
self.holderViewLeadingConstraint.constant = 320;
}
#pragma mark - Setters
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
[super setBackgroundColor:backgroundColor];
self.leftBarButtonContainerView.backgroundColor = backgroundColor;
self.rightBarButtonContainerView.backgroundColor = backgroundColor;
}
- (void)setLeftBarButtonItem:(UIButton *)leftBarButtonItem
{
[super setLeftBarButtonItem:leftBarButtonItem];
}
- (void)setLeftBarButtonItemWidth:(CGFloat)leftBarButtonItemWidth
{
// self.leftBarButtonContainerViewWidthConstraint.constant = leftBarButtonItemWidth;
[self setNeedsUpdateConstraints];
}
- (void)setRightBarButtonItem:(UIButton *)rightBarButtonItem
{
[super setRightBarButtonItem:rightBarButtonItem];
}
- (void)setRightBarButtonItemWidth:(CGFloat)rightBarButtonItemWidth
{
// self.rightBarButtonContainerViewWidthConstraint.constant = rightBarButtonItemWidth;
[self setNeedsUpdateConstraints];
}
- (void)setRightContentPadding:(CGFloat)rightContentPadding
{
// self.rightHorizontalSpacingConstraint.constant = rightContentPadding;
[self setNeedsUpdateConstraints];
}
- (void)setLeftContentPadding:(CGFloat)leftContentPadding
{
// self.leftHorizontalSpacingConstraint.constant = leftContentPadding;
[self setNeedsUpdateConstraints];
}
#pragma mark - UIView overrides
- (void)setNeedsDisplay
{
[super setNeedsDisplay];
[self.textView setNeedsDisplay];
}
//return the custom view that we are going to create next
- (JSQMessagesToolbarContentView_custom *)loadToolbarContentView
{
NSArray *nibViews = [[NSBundle bundleForClass:[JSQMessagesToolbarContentView_custom class]] loadNibNamed:NSStringFromClass([JSQMessagesToolbarContentView_custom class]) owner:nil options:nil];
return nibViews.firstObject;
}
after this you need to create add new .xib file name it as JSQMessagesToolbarContentView_custom.xib this file contains our small content view for the inputToolbar and more importantly set the out let connections as doing the demo example and aslo set the view class name to JSQMessagesToolbarContentView_custom. hear i can only add image of the custom view.
now create a outlet for leading constraint to reduce the size of the content view as give below,
and add the constraints outlet's as given in the demo. so if u add some constrians without modifying the base class it will give error or runtime error so edit the base class also
now go to JSQMessagesToolbarContentView.h and add the blow properties form JSQMessagesToolbarContentView.m just cut and past and make it public.
#interface JSQMessagesToolbarContentView : UIView
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftBarButtonContainerViewWidthConstraint;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightBarButtonContainerViewWidthConstraint;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftHorizontalSpacingConstraint;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightHorizontalSpacingConstraint;
//...rest of the code
now in the JSQMessagesInputToolbar.m file, in order to make the tool bar transperent for half of the splitview,
- (void)awakeFromNib
{
[super awakeFromNib];
//...rest of the code
[self setBackgroundImage:[UIImage new]//imageNamed:#"topbar"]
forToolbarPosition:UIToolbarPositionAny
barMetrics:UIBarMetricsDefault];
[self setShadowImage:[UIImage new] forToolbarPosition:UIBarPositionAny];
[self setBackgroundColor:[UIColor clearColor]];
}
thats it now run the project, and change the leading constraints constant you see below output,

Subviews for a UIViewController are not showing

I am attempting to create a UI programmatically. My problem is the view controllers subviews are not showing.
Here is how I am trying to create the UI:
.h
#import <UIKit/UIKit.h>
#protocol TimerCreatorDelegate <NSObject>
- (void)timerCreatorControllerDismissedWithString:(NSString *)timerName andTimeInterval:(NSTimeInterval)timeInterval;
#end
#interface TimerCreatorController : UIViewController {
// id timerCreatorDelegate;
}
#property (nonatomic, assign) id<TimerCreatorDelegate> timerCreatorDelegate;
#property (weak, nonatomic) UIDatePicker *timerLength;
#property (weak, nonatomic) UITextField *timerNameTextField;
#property (weak, nonatomic) UIButton *createTimerButton;
//#property (weak, nonatomic) IBOutlet UIDatePicker *timerLength;
//#property (weak, nonatomic) IBOutlet UITextField *timerNameTextField;
- (IBAction)createTimer:(id)sender;
#end
.m
#import "TimerCreatorController.h"
#interface TimerCreatorController ()
#end
#implementation TimerCreatorController
#synthesize timerCreatorDelegate;
#synthesize timerNameTextField;
#synthesize timerLength;
#synthesize createTimerButton;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
timerNameTextField.placeholder = #"This timer is for:";
timerLength.datePickerMode = UIDatePickerModeCountDownTimer;
[createTimerButton setTitle:#"Start Timer" forState:UIControlStateNormal];
[createTimerButton setTitleColor:[UIColor colorWithRed:46/255 green:134/255 blue:53/255 alpha:1] forState:UIControlStateNormal];
createTimerButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (void)viewWillLayoutSubviews {
printf("viewWillLayoutSubviews Called.\n\n");
[self.view addSubview:timerLength];
[self.view addSubview:timerNameTextField];
[self.view addSubview:createTimerButton];
timerLength.translatesAutoresizingMaskIntoConstraints = false;
timerNameTextField.translatesAutoresizingMaskIntoConstraints = false;
createTimerButton.translatesAutoresizingMaskIntoConstraints = false;
timerLength.frame = CGRectMake(0, 0, self.view.frame.size.width, 216);
timerNameTextField.frame = CGRectMake((self.view.frame.size.width - 300) / 2, timerLength.frame.size.height + 40, 300, 30);
createTimerButton.frame = CGRectMake((self.view.frame.size.width - 96) / 2, timerNameTextField.frame.origin.y - 40, 96, 36);
[NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:
[timerLength.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[timerLength.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[timerLength.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
[timerLength.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[timerNameTextField.topAnchor constraintEqualToAnchor:timerLength.bottomAnchor],
[timerNameTextField.rightAnchor constraintEqualToAnchor:self.view.rightAnchor constant:-37],
[timerNameTextField.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:37],
// [timerNameTextField.heightAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:30],
[createTimerButton.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
// [createTimerButton.widthAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:96],
// [createTimerButton.heightAnchor constraintEqualToAnchor:NSLayoutAttributeNotAnAttribute constant:36],
[createTimerButton.topAnchor constraintEqualToAnchor:timerNameTextField.bottomAnchor]
#warning Finish Layout Constraints
, nil]];
NSString *timerLengthString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(timerLength.frame)];
NSString *timerNameTextFieldString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(timerNameTextField.frame)];
NSString *createTimerButtonString = [NSString stringWithFormat:#"%#", NSStringFromCGRect(createTimerButton.frame)];
printf("timerLength: %s \n", [timerLengthString UTF8String]);
printf("timerNameTextFieldString: %s \n", [timerNameTextFieldString UTF8String]);
printf("createTimerButtonString: %s \n", [createTimerButtonString UTF8String]);
}
- (IBAction)createTimer:(id)sender {
if ([self.timerCreatorDelegate respondsToSelector:#selector(timerCreatorControllerDismissedWithString:andTimeInterval:)]) {
[self.timerCreatorDelegate timerCreatorControllerDismissedWithString:timerNameTextField.text andTimeInterval:timerLength.countDownDuration];
}
[self dismissViewControllerAnimated:true completion:nil];
}
#end
Despite me setting the frames of my views, the prints are showing frames of {{0,0},{0,0}}.
Where do you create the subviews? I see you trying to use them, but not where you are actually creating the objects you are using. Is it a .Nib file or are you actually doing everything programmatically? And why are your subviews weak references? If they are not held by anything else they will be released after they are created...
In Swift it will crash when you try to use an object before it is initialized. Objective-C is a little more forgiving and will just do nothing if you tell it to use a nil object (at least in the instances you are using).

IOS 8 xCode 6 button appears disabled but is not

Environment: IOS 8.02 Xcode 6
I have a form on a StoryBoard. It has 2 labels a text box and 2 buttons.
btnValidateAndSave disables all of the other controls on the form and calls a web service.
btnCance goes grey as if it has been disabled however it is clickable.
Below is the actual code behind the form.
#import "vcDigitalKeyManager.h"
#import "serviceManager.h"
#import "eziEnums.h"
#import "IsSystemLockedMethod.h"
#import "eziResponse.h"
#interface vcDigitalKeyManager ()
- (IBAction)btnValidateAndSave:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *txtDigitalKey;
#property (strong, nonatomic) IBOutlet UILabel *lblErrorMessage;
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *lblValidateActivity;
#property (strong, nonatomic) IBOutlet UIButton *btnValidateAndSave;
#property (strong, nonatomic) IBOutlet UIButton *btnCancel;
#property (strong, nonatomic) NSNumber *isWorking;
- (IBAction)btnCancel:(id)sender;
#end
#implementation vcDigitalKeyManager
#synthesize txtDigitalKey = _txtDigitalKey;
#synthesize lblErrorMessage = _lblErrorMessage;
#synthesize btnCancel = _btnCancel;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.lblValidateActivity setHidden:true];
}
- (IBAction)btnValidateAndSave:(id)sender {
[self.txtDigitalKey resignFirstResponder];
[self.txtDigitalKey setEnabled:NO];
[self.txtDigitalKey setUserInteractionEnabled:NO];
[self.lblValidateActivity setHidden:NO];
[self.btnValidateAndSave setEnabled:NO];
[self.btnValidateAndSave setUserInteractionEnabled:NO];
[self.txtDigitalKey setEnabled:NO];
[self.txtDigitalKey setUserInteractionEnabled:NO];
self.btnCancel.enabled=NO;
self.btnCancel.userInteractionEnabled = NO;
[self.lblValidateActivity startAnimating];
_isWorking = [NSNumber numberWithInt:1];
NSString * requestBody = [serviceManager buildSoapMessage:IsSystemLocked :self.txtDigitalKey.text ];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(displayEziResponse:)
name:#"kIsSystemLockedResponseNotification"
object:nil];
[[[IsSystemLockedMethod alloc]init] callNonPciMethod:requestBody servicetype:NonPci];
}
- (void) displayEziResponse:(NSNotification *) notification{
NSDictionary *userInfo = notification.userInfo;
eziResponse *myResponse = [userInfo objectForKey:#"someKey"];
if([myResponse.ErrorNumber isEqual:#"0"])
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else
{
[self.lblErrorMessage setText:[NSString stringWithFormat:#"%#: %#",myResponse.ErrorNumber, myResponse.ErrorMessage]];
[self.lblErrorMessage setTextColor:[UIColor redColor]];
[self.lblErrorMessage setHidden:NO];
[self.lblValidateActivity stopAnimating];
[self.lblValidateActivity setHidden:YES];
[self.btnValidateAndSave setEnabled:YES];
[self.btnValidateAndSave setUserInteractionEnabled:YES];
[self.txtDigitalKey setEnabled:YES];
[self.txtDigitalKey setUserInteractionEnabled:YES];
[self.btnCancel setEnabled:YES];
[self.btnCancel setUserInteractionEnabled:YES];
}
}
- (IBAction)btnCancel:(id)sender {
//NSLog([NSString stringWithFormat:#"%d",NSStringFromBOOL(BOOL self.btnCancel.enabled)]);
if(_isWorking == [NSNumber numberWithInt:0])
{
[self dismissViewControllerAnimated:YES completion:nil];
}
//if(self.btnCancel.enabled)
}
#end
I have tested this in the simulator and on an iPhone 5S
Any Ideas how to actually disable the button?
Prefer also disabling user interaction on the button !
[self.btnCancel setUserInteractionEnabled:NO];
I found the issue and it was not actually the button that was the issue.
The Validate and Save button calls a function that does an async call to a web service.
I am using notifications to let the main thread know when the response has been received.
The notification was being sent back on the background thread. The affect of this was that the Cancel button was being enabled in real time but not the visual state.
I modified the code so the notification is sent back to the main thread and now everything updates on the main thread within 1 second.

UIKeyboard behind Picker View

I have Two text field in my View. i am using picker view as input. when ever picker is enabled the keyboard is visible behind the picker.The other issue is when i use Resign First responder for the text field in the Action the first time it shows picker without the keyboard at behind.But the second time when i click the text field the KeyBoard appears instead of picker. Here is my code.
- (IBAction)selectService:(id)sender
{
[self createActionSheet];
//[selectServiceTextBox resignFirstResponder];
pickerType = #"servicePickerType";
servicePicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
pickerArray = [[NSArray alloc]initWithObjects:#"Money Transfer",#"Bill Payment", nil];
servicePicker.dataSource = self;
servicePicker.delegate = self;
servicePicker.showsSelectionIndicator = YES;
[actionSheet addSubview:servicePicker];
// rowIndex = [stateTextField.text intValue];
//[servicePicker selectRow:rowIndex inComponent:0 animated:NO];
}
- (IBAction)wayOfTransfer:(id)sender
{
if ([selectedItem isEqualToString:#""])
{
NSLog(#"empty selection");
}
else if ([selectServiceTextBox.text isEqualToString:#"Money Transfer"])
{
[self createActionSheet];
// [secondTextBox resignFirstResponder];
//[selectServiceTextBox resignFirstResponder];
pickerType = #"MoneyTransferMethod";
servicePicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
pickerArrayTwo = [[NSArray alloc]initWithObjects:#"Cash Pick-up",#"Bank Account",#"Zym Card", nil];
servicePicker.dataSource = self;
servicePicker.delegate = self;
servicePicker.showsSelectionIndicator = YES;
[actionSheet addSubview:servicePicker];
}
}
You have to make the picker and keyboard appear exclusive of each other so that both can gracefully be switched between each other.. One way to do it is make the picker hidden by default and then bring it on screen when the first textview is on focus using an animation... Similarly hide it while bringing keyboard on using the second textview.
I have made a sample project that does this and have tested it... I hope this is what you intend to do...
//ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITextViewDelegate,UIPickerViewDataSource,UIPickerViewDelegate>
#property (weak, nonatomic) IBOutlet UITextView *firstTextView;
#property (weak, nonatomic) IBOutlet UITextView *secondTextView;
#property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
#end
//ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController{
NSMutableArray *pickerDataSource;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
pickerDataSource = [[NSMutableArray alloc] initWithObjects:#"data1",#"data2", nil];
self.pickerView.frame = CGRectMake(0, self.view.frame.size.height, 320, 162);
self.pickerView.delegate = self;
self.pickerView.dataSource = self;
}
// Textview delegates
-(void)textViewDidBeginEditing:(UITextView *)textView{
if ([textView isEqual:self.firstTextView]) {
[textView resignFirstResponder];
if (self.pickerView.frame.origin.y >= self.view.frame.size.height) {
[self showPicker];
}else{
[self hidePicker];
}
}else{
[self hidePicker];
[self.secondTextView becomeFirstResponder];
}
}
-(void)showPicker{
[UIView animateWithDuration:0.2 animations:^{
self.pickerView.frame = CGRectMake(self.pickerView.frame.origin.x, self.pickerView.frame.origin.y - self.pickerView.frame.size.height, self.pickerView.frame.size.width, self.pickerView.frame.size.height);
}];
}
-(void)hidePicker{
[UIView animateWithDuration:0.2 animations:^{
self.pickerView.frame = CGRectMake(self.pickerView.frame.origin.x, self.pickerView.frame.origin.y + self.pickerView.frame.size.height, self.pickerView.frame.size.width, self.pickerView.frame.size.height);
}];
}
// Picker Delegates
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return pickerDataSource.count;
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
self.firstTextView.text = pickerDataSource[row];
}
-(NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return [pickerDataSource objectAtIndex:row];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Please let me know if there is anything unclear.
i think you need hide keyboard behind picker view ... simple you put this code in your text click action method
UItextfield delegate method.....
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[text_box_variable resignFirstResponder];
}

UIViewController won't be deleted

I'm struggling with a retaining issue between two of my UIViewControllers. The view controllers are never deleted causing my app memory to keep growing memory consumption.
UITitleScreenViewController is my initial view controller. When I go from it to UIChooseAntViewController (a choose player screen) I want to relinquish ownership of UITitleViewController but as you can see in the instruments below the controller is still retained after the transition:
The second image is the retain/release history. All entries prior to #133 were issued on the app startup. I believe #133 and #140 are pairs created by the storyboard segue. So whose responsibility is to issue that extra release to destroy the controller? I tried to set self.view = nil on my willDidDisappear method but no deal.
Not only it is not releasing the controllers but it is creating new instances of them each time a transition. For instance, when I come back from ChooseAnt to Title it creates another instance of UITitleViewController!
Things that are important to say:
1) NSZombies flag is not ticked in the target scheme
2) There are no blocks in my UITitleViewController, and I commented out all blocks in UIChooseAntController. In fact these controllers are very simple. UITitle is entirely defined via storyboard (just a view with a background and two buttons performing segues)
while UIChooseAnt is a control that presents a background and a swipe interface to display available characters and radio buttons. The segue is performed programatically by calling [self performSegueWithIdentifier];
3) I don't know if this matters but the segues are defined as modal and have no animation.
EDIT: 4) None of the the controllers reference each other.
Below is the source code for the TitleViewController
This problem is driving me crazy. If anyone could shed some light on it. Anything would be of great help! Thanks!
#interface SMTitleScreenViewController ()
#property (weak, nonatomic) IBOutlet UIButton *buttonPlay;
#property (weak, nonatomic) IBOutlet UIButton *buttonCamera;
- (IBAction)onButtonPlay:(id)sender;
- (IBAction)onButtonCamera:(id)sender;
#end
#implementation SMTitleScreenViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor* color = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
UIFont* font = [UIFont fontWithName:#"Jungle Roar" size:BUTTON_FONT_SIZE];
NSString* playString = NSLocalizedString(#"Play", #"");
NSString* cameraString = NSLocalizedString(#"Camera", #"");
[self.buttonPlay setTitle:playString forState:UIControlStateNormal];
[self.buttonPlay setTitle:playString forState:UIControlStateHighlighted];
[self.buttonPlay setTitleColor:color forState:UIControlStateNormal];
[self.buttonPlay setTitleColor:color forState:UIControlStateHighlighted];
self.buttonPlay.titleLabel.font = font;
[self.buttonCamera setTitle:cameraString forState:UIControlStateNormal];
[self.buttonCamera setTitle:cameraString forState:UIControlStateHighlighted];
[self.buttonCamera setTitleColor:color forState:UIControlStateNormal];
[self.buttonCamera setTitleColor:color forState:UIControlStateHighlighted];
self.buttonCamera.titleLabel.font = font;
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (IBAction)onButtonPlay:(id)sender
{
}
- (IBAction)onButtonCamera:(id)sender
{
}
EDIT: UIChooseAntViewController (as requested)
#interface SMChooseAntViewController ()
#property (strong, nonatomic) UIImageView* rope;
#property (strong, nonatomic) UIImageView* antFrontLayer;
#property (strong, nonatomic) UIImageView* antBackLayer;
#property (strong, nonatomic) NSArray* antFrontImages;
#property (strong, nonatomic) NSArray* antBackImages;
#property (strong, nonatomic) NSArray* antNameImages;
#property (strong, nonatomic) UIButton* leftButton;
#property (strong, nonatomic) UIButton* rightButton;
#property (strong, nonatomic) UIButton* confirmButton;
#property (nonatomic) NSUInteger selectedAntID;
#property (strong, nonatomic) UIImage* radioImageHighlighted;
#property (strong, nonatomic) UIImage* radioImage;
#property (strong, nonatomic) NSMutableArray* radioViews;
#property (weak, nonatomic) IBOutlet UILabel *antDescriptionLabel;
#property (weak, nonatomic) IBOutlet UIImageView *antDescriptionBG;
#property (strong, nonatomic) UIImageView* antNameView;
#property (strong, nonatomic) UISwipeGestureRecognizer* leftSwipeRecognizer;
#property (strong, nonatomic) UISwipeGestureRecognizer* rightSwipeRecognizer;
- (void) onArrowButton:(id)sender;
- (void) onConfirmButton:(id)sender;
- (void) respondToSwipe:(UISwipeGestureRecognizer*)recognizer;
#end
#implementation SMChooseAntViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// Needed to come in between front and back player image layers
UIImage* ropeImage = [UIImage imageNamed:ROPE_IMAGE_PATH];
self.rope = [[UIImageView alloc] initWithImage:ropeImage];
self.rope.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height / 2.0f);
UIColor* brownColor = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
self.antDescriptionLabel.textColor = brownColor;
self.antDescriptionLabel.numberOfLines = 0;
NSArray* antNames = [SMProfile antNames];
// Cache available Player Views in a NSArray
UIImage* frontImages[MAX_AVAILABLE_ANTS];
UIImage* backImages[MAX_AVAILABLE_ANTS];
UIImage* nameImages[MAX_AVAILABLE_ANTS];
for (NSUInteger i = 0; i < MAX_AVAILABLE_ANTS; ++i)
{
NSString* antName = [antNames objectAtIndex:i];
frontImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_front.png", antName]];
backImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_back.png", antName]];
nameImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_name.png", antName]];
}
self.antFrontImages = [NSArray arrayWithObjects:frontImages[0], frontImages[1], frontImages[2], nil];
self.antBackImages = [NSArray arrayWithObjects:backImages[0], backImages[1], backImages[2], nil];
self.antNameImages = [NSArray arrayWithObjects:nameImages[0], nameImages[1], nameImages[2], nil];
// Load Selected player from profile
SMProfile* profile = [SMProfile mainProfile];
self.selectedAntID = profile.antID.unsignedIntegerValue;
self.antFrontLayer = [[UIImageView alloc] initWithImage:[self.antFrontImages objectAtIndex:self.selectedAntID]];
self.antBackLayer = [[UIImageView alloc] initWithImage:[self.antBackImages objectAtIndex:self.selectedAntID]];
self.antNameView = [[UIImageView alloc] initWithImage:[self.antNameImages objectAtIndex:self.selectedAntID]];
self.antNameView.center = CGPointMake(screenSize.width / 2.0f, self.antDescriptionBG.frame.origin.y);
NSString* antDescriptionKey = [NSString stringWithFormat:#"AntDescription%lu", (unsigned long)self.selectedAntID];
self.antDescriptionLabel.text = NSLocalizedString(antDescriptionKey, #"");
self.antDescriptionLabel.numberOfLines = 0;
self.antDescriptionLabel.adjustsFontSizeToFitWidth = YES;
self.antFrontLayer.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height * 0.75f);
self.antBackLayer.center = self.antFrontLayer.center;
// Here a perform button creation, loading and positioning
// No blocks are being called
// add Target to buttons
[self.leftButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.rightButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.confirmButton addTarget:self action:#selector(onConfirmButton:) forControlEvents:UIControlEventTouchUpInside];
// Create and configure SwipeRecognizers
self.leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:self.leftSwipeRecognizer];
self.rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:self.rightSwipeRecognizer];
// Here a create a custom page control scheme. I load two radio button images
// create views and add them to the root view node.
// Add remaining view to the hierarchy
[self.view addSubview:self.antBackLayer];
[self.view addSubview:self.rope];
[self.view addSubview:self.antFrontLayer];
[self.view addSubview:self.confirmButton];
[self.view bringSubviewToFront:self.antDescriptionBG];
[self.view bringSubviewToFront:self.antDescriptionLabel];
[self.view addSubview:self.leftButton];
[self.view addSubview:self.rightButton];
[self.view addSubview:self.antNameView];
[self.view bringSubviewToFront:[self.radioViews objectAtIndex:0]];
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.rope = nil;
self.antFrontLayer = nil;
self.antBackLayer = nil;
self.antFrontImages = nil;
self.antBackImages = nil;
self.antNameImages = nil;
self.leftButton = nil;
self.rightButton = nil;
self.confirmButton = nil;
self.radioImageHighlighted = nil;
self.radioImage = nil;
self.radioViews = nil;
self.antNameView = nil;
self.leftSwipeRecognizer = nil;
self.rightSwipeRecognizer = nil;
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)onArrowButton:(id)sender
{
UIButton* button = (UIButton*)sender;
NSInteger direction = button.tag;
// if on boundaries do nothing (first ant selected and swipe left or last ant selected and swipe right)
if ((self.selectedAntID == 0 && direction == -1) || (self.selectedAntID == (MAX_AVAILABLE_ANTS - 1) && direction == 1))
{
return;
}
// Update Radio Buttons. Unselect previous and select next.
UIImageView* currRadio = [self.radioViews objectAtIndex:self.selectedAntID];
currRadio.image = self.radioImage;
self.selectedAntID = (self.selectedAntID + MAX_AVAILABLE_ANTS + direction) % MAX_AVAILABLE_ANTS;
UIImageView* nextRadio = [self.radioViews objectAtIndex:self.selectedAntID];
nextRadio.image = self.radioImageHighlighted;
self.antFrontLayer.image = [self.antFrontImages objectAtIndex:self.selectedAntID];
self.antBackLayer.image = [self.antBackImages objectAtIndex:self.selectedAntID];
self.antNameView.image = [self.antNameImages objectAtIndex:self.selectedAntID];
// here I was issuing some block to perform the swipe animation for the ant image views. I commented them and I'm just replacing the images now (3 lines above)
}
- (void)onConfirmButton:(id)sender
{
// Save player choice to profile and perform segue
SMProfile* profile = [SMProfile mainProfile];
profile.antID = [NSNumber numberWithUnsignedInt:self.selectedAntID];
[profile save];
[self performSegueWithIdentifier:#"chooseAntToStageSelect" sender:self];
}
- (void) respondToSwipe:(UISwipeGestureRecognizer *)recognizer
{
// forward swipe to onArrowButton message
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft)
{
[self onArrowButton:self.rightButton];
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionRight)
{
[self onArrowButton:self.leftButton];
}
}
#end
When presenting B view controller from A, A will not release as A is the presentingViewController (please refer to the sdk doc).
Or if A,B are sub view controller of a navigation controller, A is store int he push stack which is not removed when pushing to B.
You are pushing a view controller on to a stack hence until the last one is not popped, the controller will not be released.
To go deep into dependencies on childs read the the article below.
Greatly explained, i am sure it'll help. :)
http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

Resources