Removing and reinserting UIView into UIViewController - cocoa-touch

I have a UIViewController that contains a UIView.
Between every time the viewcontroller is displyaed, the UIView has to be cleared and the content reloaded.
The problem is that the old content still appears in the UIView.
Load data before controller becomes visible:
- (void)viewWillAppear:(BOOL)animated
{
contentView = [[ContentView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
contentView.userInteractionEnabled = NO;
[self.view addSubview:contentView];
if([self loadContentData] == NO) {
[contentView release];
return NO;
}
return YES;
}
Remove content after controller is hidden:
- (void)viewDidDisappear:(BOOL)animated
{
[contentView removeFromSuperview];
[contentView release];
}
Why is this cleanup not sufficient?

Try:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[contentView removeFromSuperView]; //releases contentView
}
You must call super's method when overriding this method.
Additionally, contentView appears to be over-released in the code you've posted which makes me believe you are retaining it possibly somewhere in your real code. If this is the case, you may have over-retained contentView, which will prevent it from being released and cleaned up from your view hierarchy.
I would suggest you explore something along these lines:
- (void)viewWillAppear:(BOOL)animated
{
contentView = [[[ContentView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)] autorelease];
contentView.userInteractionEnabled = NO;
[self.view addSubview:contentView];
if([self loadContentData] == NO) {
//[contentView release]; //ContentView is now autoreleased and will be dropped when the method exits.
return NO; //this probably has a compiler warning, since it is a (void) method
}
return YES; //this probably has a compiler warning, since it is a (void) method
}

Related

performSelector:afterdelay never called in scrollViewWillBeginDragging

I try to call a method after a delay when the user start to dragging a scrollView.
This block below is called but the action define in this performselector: is called only when I stop to drag the scrollView
- (void)viewDidLoad {
[super viewDidLoad];
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:self.view.frame];
sv.delegate = self;
sv.backgroundColor = [UIColor redColor];
[sv setContentSize:CGSizeMake(1000, 200)];
[self.view addSubview:sv];
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
NSLog(#"hey");
[self performSelector:#selector(myAction) withObject:nil afterDelay:3];
}
- (void)myAction
{
NSLog(#"Called 3secondes after begin dragging");
}
I also try with a NSTimer and in the background thread but the problem is the same...
Any Idea?
If you want to have the callback fired while you're still dragging, you must schedule it for Common Run Loop modes, like so:
[self performSelector:#selector(myAction) withObject:nil afterDelay:3 inModes:#[NSRunLoopCommonModes]];
That'll do the trick :)

Which method gets called every time a subclassed uibutton gets called?

I subclassed a UIButton, and I want to give all instances of my newButton the same background image, among other things. I thought I found the right init method in initWithCoder, but it just gets called for the first newButton. I did a little test by changing the text size to something massive, and only the first newButtons text size was changed.
I can use drawRect:, and it works just fine. But I was told that is not a good method to use.
Does anyone know which method will get called, so that I can make some adjustments?
#implementation LCHButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setDefaults];
}
return self;
}
-(id)init
{
self = [super init];
if (self) {
[self setDefaults];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setDefaults];
}
return self;
}
-(void)setDefaults
{
[self setBackgroundImage:[[UIImage imageNamed:#"submitBtn.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:0] forState:UIControlStateNormal];
self.titleLabel.font = [UIFont fontWithName:#"Museo-500" size:17];
}
#end
This will change only the first button

Paging a TableView, but DataSource/Delegates not executing

I am having problems with getting the datasource or delegate methods to execute.
I am using a Paging View in my MainViewController and it pages just fine, but when it comes to the tableview is blank and when I put a breakpoint where the datasource methods are, it never gets called.
MainViewController to Load the View
if ((NSNull *)controller2 == [NSNull null])
{
if(page == 2)
{
controller2 = [[requestDetailThreeViewController alloc] initWithRequestNumber:[request objectForKey:#"RequestID"]];
[viewControllers replaceObjectAtIndex:page withObject:controller2];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * 2;
frame.origin.y = 0;
controller2.view.frame = frame;
[scrollView addSubview:controller2.view];
}
}
The TableViewController (requestDetailThreeViewController)
- (void)viewDidLoad
{
[super viewDidLoad];
[History fullHistoryWithBlock:^(NSArray *netMessages, NSError *error) {
if (error) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", nil) message: [error localizedDescription] delegate:nil cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(#"OK", nil), nil] show];
} else {
NSLog(#"Number of Historical Entries: %d", [netMessages count]);
self.historyFromNet = [NSMutableArray arrayWithArray:netMessages];
}
} forID:requestNum];
}
- (id) initWithRequestNumber:(NSString *)requestID
{
if (self = [super initWithNibName:#"View" bundle:nil])
{
self.requestNum = requestID;
}
return self;
}
(Apologize for the spacing on the 2nd block of code, hard time getting it into code mode.
It executes these two methods, but it doesn't execute the datasource methods I also have in the class.
In the XIB file I have the owner of the xib set to the requestDetailThreeViewController
The tableView is set to datasource/delegate and view
If I set the tableview to hidden in the ViewDidLoad, it does disappear.
I just can't get it to execute the methods to populate the table.
Update:
Some more information-
The View XIB only has a TableView in it, no controllers.
Thanks!
Alan
You are adding a child view controller so you will need to use the UIViewController containment methods.
Something like:
controller2 = [[requestDetailThreeViewController alloc] initWithRequestNumber:[request objectForKey:#"RequestID"]];
[viewControllers replaceObjectAtIndex:page withObject:controller2];
[self addChildViewController:controller2];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * 2;
frame.origin.y = 0;
controller2.view.frame = frame;
[scrollView addSubview:controller2.view];
[controller didMoveToParentViewController:self];
}
Also consider using a UIPageViewController for a paging view rather than doing it manually via a scroll view
You can try to set self.tableView.delegate = self in -(void)viewDidLoad
that is
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
}

How to get rid of EXC_BAD_ACCESS

So, another EXC_BAD_ACCESS topic on StackOverflow, but as I'm new to Objective-C this is still a topic I don't really grasp yet. Even though I have done a lot of research about it already.
The issue is the following. I have a UIScrollView that I have overwritten using a Custom Class (named MultiSelectView). If the user taps this UIScrollView and then I want to open a view that allows him to select some data.
So I have declared a UITapGestureRecognizer that calls the openMultiSelect: method. But on the line [parent.navigationController pushViewController:view animated:YES]; I get a Program received signal: "EXC_BAD_ACCESS". error. Why o why?
- (id) initWithCoder:(NSCoder *) coder {
self = [super initWithCoder: coder];
if (self) {
// Add a Tap Gesture Recognizer to the Scrollview.
// If the user taps the view, it triggers the 'openMultiSelect' method.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(openMultiSelect:)];
[singleTap setNumberOfTapsRequired:1];
[singleTap setNumberOfTouchesRequired:1];
[self addGestureRecognizer:singleTap];
}
return self;
}
- (void)openMultiSelect:(UIGestureRecognizer *)gesture {
//int myViewTag = gesture.view.tag; // now you know which view called
DataSelectView *view = [[DataSelectView alloc] initWithNibName:#"DataSelectView" bundle:[NSBundle mainBundle]];
view.allowMultiSelect = YES;
[parent.navigationController pushViewController:view animated:YES];
[view release];
}
So the parent that you see is a ViewController that contains the tab. Is there a better way to do this? Because for now I have thus a ViewController that contains Tabs. In its activateTab: method I thus create the tab and pass self along. I do the same in the viewDidLoad for that tab to pass the parent to the custom UIScrollView:
- (void) activateTab:(int)index {
... code ...
self.tab_Basic = [[TabBasic alloc] initWithNibName:#"TabBasic" bundle: [NSBundle mainBundle]];
self.tab_Basic.parent = self;
... code ...
}
You should make some change to your callback method. Something like that:
- (void)openMultiSelect:(UIGestureRecognizer *)gesture {
//int myViewTag = gesture.view.tag; // now you know which view called
if(gesture.state == UIGestureRecognizerStateEnded){
DataSelectView *view = [[DataSelectView alloc] initWithNibName:#"DataSelectView" bundle:[NSBundle mainBundle]];
view.allowMultiSelect = YES;
[parent.navigationController pushViewController:view animated:YES];
[view release];
}
}
What you are doing wrong is releasing the object "view" too early, don't release it until the view is popped. That should fix the problem.
- (void)openMultiSelect:(UIGestureRecognizer *)gesture {
//int myViewTag = gesture.view.tag; // now you know which view called
DataSelectView *view = [[DataSelectView alloc] initWithNibName:#"DataSelectView" bundle:[NSBundle mainBundle]];
view.allowMultiSelect = YES;
[parent.navigationController pushViewController:view animated:YES];

UIView, UIScrollView and UITextFields problem calling Method

I have a view with several embedded UITextFields, this UIView is subordinated to a UIScrollView in IB. Each text field is supposed to invoke a method called updateText defined in the viewcontroller implementation file when the user is done editing the field. For some reason, the method updateText never gets invoked. Anyone have any ideas how to go about fixing this? The method fired off just fine when the UIScrollView was not present in the project but the keyboard would cover the text fields during input, which was annoying. Now my textfields move up above the keyboard when it appears, but won't fire off the method when done editing.
Here is my implementation file:
#import "MileMarkerViewController.h"
#implementation MileMarkerViewController
#synthesize scrollView,milemarkerLogDate,milemarkerDesc,milemarkerOdobeg,milemarkerOdoend,milemarkerBusiness,milemarkerPersonal,milemarker;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Initialization code
}
return self;
}
- (BOOL) textFieldShouldReturn: (UITextField*) theTextField {
return [theTextField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWasShown:)
name: UIKeyboardDidShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWasHidden:)
name: UIKeyboardDidHideNotification
object: nil];
keyboardShown = NO; // 1
[scrollView setContentSize: CGSizeMake( 320, 480)]; // 2
}
- (void)keyboardWasShown:(NSNotification*)aNotification {
if (keyboardShown) return;
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [scrollView frame];
viewFrame.size.height -= keyboardSize.height;
scrollView.frame = viewFrame;
// Scroll the active text field into view.
CGRect textFieldRect = [activeField frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];
keyboardShown = YES;
}
- (void)keyboardWasHidden:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollView frame];
viewFrame.size.height += keyboardSize.height;
[scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
scrollView.frame = viewFrame;
keyboardShown = NO;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeField = nil;
}
- (IBAction)updateText:(id) sender {
NSLog(#"You just entered: %#",self.milemarkerLogDate.text);
self.milemarker.logdate = self.milemarkerLogDate.text;
self.milemarker.desc = self.milemarkerDesc.text;
self.milemarker.odobeg = self.milemarkerOdobeg.text;
self.milemarker.odoend = self.milemarkerOdoend.text;
self.milemarker.business = self.milemarkerBusiness.text;
self.milemarker.personal = self.milemarkerPersonal.text;
NSLog(#"Original textfield is set to: %#",self.milemarker.logdate);
[self.milemarker updateText];
}
- (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)dealloc {
[super dealloc];
}
#end
I think you have already known a solution because of post date.
You can implement textFieldShouldReturn method and resign first responder from textField in the method.
textFieldDidEndEditing is called after textField resign first responder.