I have a UIViewController that is initialised with a correct frame, however somewhere in my code the frame gets mangled and I'm having difficulty finding out where.
In situations like this it is usually handy to watch a variable in the debugger, however I have no way of accessing the controller->view->frame property in my variable view, since it isn't a variable, it's a property (surprisingly enough)
Drilling into the UIView in the variables display shows a few things but nothing I can relate to the frame, I thought perhaps that would be in layer but it isn't.
Is there any way to watch for changes in a private API? I guess not, since the variables are essentially 'hidden' and so you can't specify exactly what to watch.
Alternatively, what other approach could I use? I already tried subclassing UIView, setting my UIViewController's view to point to this subclass and breaking on the setFrame method but it didn't seem to work.
EDIT: the subclassing UIView method DID work, I just had to set the view to point to my test subclass in viewDidLoad and not the init method. Leaving this question open as I'm not sure if this is the best way of approaching this kind of problem...
Subclass your the view you want to track and rewrite the setFrame method:
#implementation MyTableView
- (void)setFrame:(CGRect)frame;
{
NSLog(#"%#", frame);
[super setFrame:frame];
}
#end
Then use the debugger to add a breakpoint to it and check when it gets called. Eventually, you'll see when the frame gets changed and where does the change comes from.
I discovered this can be done using key value observers.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html
You could create an ivar, view2, and just assigned it to your view in your loadView method. That should enable you to watch it like a normal variable.
Related
I'm fairly new to Cocoa Touch. Right now I'm trying to subclass UIViewController to provide my custom view. Since I intend to save the content of a UITextField (passcodeField) using NSUserDefaults, I want to be notified whenever the UITextField changes its value.
I've read somewhere that in order to do that I should add the view controller to be an observer of the UITextFieldTextDidChangeNotification notification. However I'm just not sure when to do that. I've considered several options.
In the -loadView method. However, since I'm loading my view using a XIB, I think i shouldn't mess with this method and should instead leave it as-is. (Am I correct on this point, BTW? )
In the -viewWillAppear method. But this method may be called multiple times because the view may be moved out and into the screen without being destroyed and recreated. (Am I correct? ) This will not do any harm to the program but sure doesn't seem like the correct way.
In the initializer of the UIViewController. If I want to add the notification there I must reference the UITextField. By doing this I essentially cause the view to created before it is really needed. Also I think I read somewhere that if the system runs low on memory the offscreen views may be destroyed. Thus I may lose the notification observing if such thing happens, right?
So I'm totally confused right now. Could you guys give me some advice of where to put it? Thanks so much!
Put it in the - (void)viewDidLoad method of your ViewController remember to call [super viewDidLoad]; at the start of your implementation.
This is driving me nuts. I am using three20's TTTableViewController and when I get a memory warning, the screen goes white. Now, after reading on the three20 google group is seems that the tableView got released. But, I cannot for the life of me figure out a check to see if that is the case, then create it again.
I was using the following because I thought it would fix the issue, but it seems that it doesn't satisfy the if statement:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// If we don't have a datasource we need to reset it
if (!self.dataSource) {
// Create datasource again
}
}//end
Does anyone know what to do when this happens? The google group has been no help.
Are you subclassing TTTableViewController? I haven't used it before, but assuming it's just like UITableViewController...
How does your "viewDidUnload" look like? Are you releasing the tableview here? If so, you need to create tableview in viewDidLoad to match it.
No need to check if dataSource is available in viewDidAppear, because if you read View programming guide, it explains that memory warning will call "viewDidUnload" to give you a chance to clean up data that are created in "viewDidLoad".
i had the same issue and it drove me crazy as well.
Nobody mentions it in the three20 docs, but you shouldn't use UIViewController's initWithNibName function to add subviews. If you do, a memory warning will release these subviews.
Try to move your code from initWithNibName function to viewDidLoad function. I have noticed that some code need to be kept in the initWithNibName, such as navigation styles. However, any subviews added to the controller's view should be in the viewDidLoad function.
In general you should be careful to set up views in viewDidLoad rather than the class constructor. For instance, you should set up your launcher view in viewDidLoad rather than the constructor of your launcher view controller, otherwise your launcher will become empty after a memory warning.
In the case of TTTableViewController however this does not (usually) apply because you don't set up the table view manually. I had the same problem you had, and eventually tracked it down: I had redefined viewWillDisappear: and forgot to call [super viewWillDisappear:animated]. This meant that some of the flags that the Three20 controller maintains about the state of the view were not updated correctly.
I also found that it was beneficial to redefine didReceiveMemoryWarning to call [self setEditing:NO] before calling super; I found that the state of the table view got confused otherwise (this is not relevant if you don't use edit mode for your table).
Finally, there is a bug in Three20 which means that tables in loading/empty/error mode will not be restored properly; see a discussion in the blog post by TwoCentStudios and a proposed fix on github.
I am trying to get certain iVars from my view controller to my view (a subview) so drawrect will draw a shape based on user inputs. When I hard code those variables into the subview, the shape draws perfect, but when I use getters to access the custom variables from the view controller, they come back nil and the drawing is messed up. I get no errors or warnings. I know there is a problem with the getter, any suggestions? Anybody have an example of passing variables to a subview so cgcontext can be used in the drawrect? I am stuck big time. I assume at this point my problem is so simple, I am missing something fundamental. I thought I had setters/getters down packed.
Make sure that whatever you're getting 'from' (ie, the parent, or view, or whatever you're accessing the getters on) is not nil. Try setting a break point right before accessing them and validating that your targets are valid, and have valid data.
We'd really need to see more code. Ben was saying that, somewhere in your view, you must set parent = (some code to get a reference to my parent). Make sure this actually works, i.e. you really do have a reference to your parent. Even if the ivars are correct, if your parent is nil, then sending [nil getSomeiVar] will return 0 in most cases.
Without a better understanding of the code, this is my guess. Somewhere in your UIViewController, you should have something like:
myView = //make your view
[myView setParent:self]
Parent is a variable of the view, with a property so that you can access it
Edit:
Why does it matter if we know what you are making?
For setParent to work, you need to following:
an ivar YourUIViewControllerSubclass *parent; in your view's .h
a property for this ivar #property (nonatomic, retain) YourUIViewControllerSubclass *parent; in your view's .h
A synthesis for this ivar #synthesize parent; in your view's .m inside the #implementation
Yeah, you were both wrong, but I took a lot away from you helping me though. I didn't have to post code after all. I had to pick up my debugging skills big time. Someone in another post mentioned violations of the MVC. I had to set the ivars in the uiview, to the custom values FROM the view CONTROLLER. In other words, I had to set from the controller rather than getting from the subview.
I have a UIView subclass that I manipulate a lot of graphics on based on touches. All the [self setNeedsDisplay] calls seem to be fine.
However, I used an instance variable pointer to my same UIView subclass instance, and then tried manipulating it and then calling [UIViewSubClass setNeedsDisplay] from another UIView class, and DrawRect is never being called. Are there restrictions to where you can call setNeedsDisplay from?
(This method is called when a button is clicked on another UIView subclass. The method is being called, but not DrawRect)
-(IBAction)loadGrid2;
{
tempSoundArray = musicGridView1.soundArray;
[musicGridView1.soundArray setButtonArrayToNull];
[musicGridView1 setNeedsDisplay];
musicGridView1.soundArray = tempSoundArray;
NSLog(#"loadGrid2 was called");
}
drawRect: will only be called when it makes sense; ie, the view must be visible, onscreen, and dirty. Is your drawRect: ever called? It should be called when the view is first brought onscreen as well.
To add to Ben:
This most likely means that you have problems elsewhere. Your pointer may not be nil or otherwise invalid or the view may not be added to the hierarchy properly.
You may want to consider not handling this type behavior within the view and instead in the view controller. Control behavior and save presentation state in the view controller and don't subclass the view classes. It will simplify your code with less "pointer passing". This will also make it easier to debug this type of problem.
If you feel your view controller is getting bloated, consider splitting the responsibilities up among multiple view controllers.
I have a pretty simple UIViewController. It's initialized with a view I've created in Interaface Builder, which contains only a UIImageView. When the user touches the screen, I want the touchesBegan message of UIViewController to get called. So, I override it and added some logging, but nothing has happened.
I haven't done anything "special" at all, as since UIViewController inherits from UIResponder, I expect this to work right out of the box. From what I understand UIImageViews have user interaction disabled by default, so I have enabled it, both via InterfaceBuilder and in my UIViewcontroller's viewDidLoad method (I have tied the UIImageView to an IBOutlet). I also am ensuring that userInteraction is enabled in the parent view in Interface Builder.
Anything else that I am forgetting here?
OK, I'm a dummy. It works fine. The problem was, I didn't realize I was sending a release message to the UIViewController without having retained it elsewhere first. So that was causing the problem.
It is hard to say what your problem is, I don't know what you mean by overriding it?
Make sure you are connecting the touchesBegan event with an IBAction in Interface Builder?
You must create a IBAction function to handle the event, and explicitly connect the even to the IBAction. It is not as simple as simply overriding a method.
Although you have the View tied to an IBOutlet, you need to connect the event using an IBAction, or else you won't get any of those events.
Please ignore the first answer. It is, in fact, as easy as overriding the method in the custom view. The most common reason for not receiving touchesBegan is the canBecomeFirstResponder method not being implemented. This is not an IB hookup, these are the standard methods for touch handling.