I just tracked down a crash I was having in my iOS app and it is related to willAnimateRotationToInterfaceOrientation being called before viewWillAppear.
I have an app with two views. Basically, when view1 disappears, I release some arrays, assuming they will be re-initialized when it re-appears in viewWillAppear.
However, if I change orientation in view2 then switch back to view1, this causes willAnimateRotationToInterfaceOrientation to happen before view1 has calledviewWillAppear and re-initialized everything and this causes a crash.
Is there any way to delay willAnimateRotationToInterfaceOrientation until after the view has appeared and everything has been re-initialized?
If not, the only solutions I can see are either not using willAnimateRotationToInterfaceOrientation (which results in an ugly looking orientation change) or not releasing my data when I switch from view1 to view2 which results in more memory being used than necessary.
Does anybody have any thoughts on what I should do?
You can use lazy-loading style code avoid your problem, like:
NSArray* someData;
-(void)somefun1{
if (!someData) {
[self loadData];
}
//use your data
}
-(void)somefun2{
if (!someData) {
[self loadData];
}
//use your data
}
-(void)loadData{
//some loading code
}
use lazy-loading style code you never need mind event calling order.
Related
It's called only once, like viewDidLoad
It's called right before viewWillAppear. So it's called after the UIViewController navigationController is no longer empty.
Basically I want to set things up programatically stuffs for UIViewController. However, I want navigationController property to already exist. Also I want the whole thing to be called only once.
I'm not super-familiar with view lifecycles in iOS but if by some chance there is not a method that fits your description and you really do need to have such an event, you could always use a call back combined with a property, ie
- (void)viewWillAppear:(BOOL)animated {
if (!self.specialMethodHasBeenCalled) { [self doSpecialMethod]; }
// other viewWillAppear stuff to do every time
}
- (void)specialMethod {
// do stuff
self.specialMethodHasBeenCalled = YES;
}
Might need to do some more stuff for thread safety, but this is just a hacky solution.
I have two draggable UIImageViews within a UIView. When i drag one UIImageView over the other, i would like to disable touch events to those two UIImageViews and create another UIImageView which does respond to my touch events. Ive tried using setUserInteractionEnabled: but it isnt really doing anything at all. Forgive me if this is a no brainer but i am new to programming, here is what i have so far. Please give me some feedback on my code and give me some constructive criticism because i feel as though i am setting this up all wrong.
-(void)swapImageViews
{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
[self.imgView1 setUserInteractionEnabled:NO];
[self.imgView2 setUserInteractionEnabled:NO];
}
}
So once again, the goal is to swap out two image views with a fresh one that i can also drag around.
The problem i am running into is that my touch events are still moving the first two image views around behind the third one.
You say that the third image appeared, but the the other two are still in the view and they still receive touch events. There are two two scenarios for this:
You've already added the third image view somewhere else, and your method is never called, or the condition is never met.
self.imgView1 and self.imgView2 are not set to the actual objects in your view stack. They're probably just nil. you can debug that by NSLog(#"%# %#", self.imgView1, self.imgView2); in that method, before the condition, or in viewWillAppear or viewDidLoad.
The reasons that support my opinion:
In that method, you call removeFromSuperview to the two image views you want to disable, yet they are still visible and receiving touch events. That means that this method is not executed. Which means they're nil or some other objects, that are different than the visible ones.
The condition in the start of the method is a bit strict. It's hard to be easily met. Yet the method is executed as the third image view is added to the view. This can happen when the object is nil, so the values returned by methods are the default values.
Check this link "the 10th item" and this one too.
Well this is not the best way to do that, but with the code that you provide to us, we have not a lot of options, you can do something like that, to remove the gestures:
-(void)swapImageViews{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
NSArray* gestures1 = [self.imgView1.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures1){
[self.imgView1 removeGestureRecognizer:gesture];
}
NSArray* gestures2 = [self.imgView2.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures2){
[self.imgView2 removeGestureRecognizer:gesture];
}
}
}
I am relatively new to iOS development. I developed an app for Android first, and now am porting it to iOS. One thing that I can't figure out is how to make a ViewController "go away". I'm accustomed to the finish() method in Android. With that method, the current activity ends itself and the user is presented with the the previous screen that was open prior to opening the current screen.
What I'm trying to accomplish is making my "create" screen go away after a record is saved. In the Android world, I would just call the finish() method and that would be taken care of. What is the iOS equivalent?
I have tried the following code in my iOS app, hoping that the view would be animated away.
[self.navigationController popToRootViewControllerAnimated:YES];
Edit:
The view was presented as below.
SettingsViewController *vc = [[SettingsViewController alloc] initWithNibName:#"SettingsView"];
controllers = [NSArray arrayWithObject:vc];
self.sideMenu.navigationController.viewControllers = controllers;
[self.sideMenu setMenuState:MFSideMenuStateClosed];
where controllers is defined as such:
NSArray *controllers = nil;
How did you present this view controller?
Did you use presentViewController:animated:completion:? If so, you want something like this:
[self dismissViewControllerAnimated: YES];
If you are using pushViewController:animated:, you are not talking about a modal view. You are talking about a normal ViewController you pushed onto the stack. To "undo" this, you need to pop the view controller:
[self.navigationController popViewControllerAnimated: YES];
Though finish() and [self dismissViewControllerAnimated:YES] are similar in terms of functionality, but they are not exactly same, when we call finish() method in the Android, we are programatically telling the Android system to destroy the activity completely from the memory, when we override the onDestroy() activity life cycle callback and add a log, then logs are shown when finish() is called. onDestroy() is also called when the system needs resources and it frees the memory by finishing the activity. But
[self dismissViewControllerAnimated: YES];
does not remove the UIViewController instance from the memory, the equivalent callback method for onDestroy() in IOS is viewDidUnload() which is not called on the message dismissViewController. I think the IOS system can only free the memory for the UIViewController instance when system needs resources but we can't programatically do that.
after pushing your viewcontroller call this method
func finish(){
var navigationArray = self.navigationController?.viewControllers //To get all UIViewController stack as Array
navigationArray!.remove(at: (navigationArray?.count)! - 2) // To remove previous UIViewController
self.navigationController?.viewControllers = navigationArray!
}
Say I have two view controllers: xVC and yVC. I have used the shake API and and have used the methods -(void)motionBegan,-(void)motionEnded: and -(void)motionCancelled in xVC. What happens is when the device is shaken, it fires a simple animation. Now the thing is that this animation is fired even when the I have yVC open that is, when yVS.view has been added as the subview. What I am looking for is some if condition which I can use in -(void)motionEnded: like this:
if(yVC == nil)
{
//trigger animation
}
By that I mean that the shake shouldn't work when yVC is visible. How do I do that? Please help.
The general advice I have seen and used is to ask a view if it has a non-nil window property:
if( ! yVC.view.window) {
// trigger animation
}
But note that this doesn't always equate with being visible; though in most apps it's about as good as you can performantly get (the basic case where it's not accurate is when a different view completely obscures it, but this may still satisfy your needs)
Add this to both of your view controllers:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
visible = YES;
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
visible = NO;
}
Now, just check the variable isVisible of both the view controllers and trigger your animation likewise.
The previous answers all work to some degree, but fail to take modally presented view controllers into account. If view controller A presents view controller B, of the previous answers will tell you that A is still visible. If you, like me, want to know whether or not the view is actually visible (and not just a part of the view hierarchy), I would suggest also checking the presentedViewController property:
if (self.isViewLoaded && [self.view window] && !self.presentedViewController) {
// User is looking at this view and nothing else
}
This works since presentedViewController will be non-nil whenever the current view controller OR any of its ancestors are currently presenting another view controller.
I have a very simple app. 1 navigationController with 2 viewControllers.
The first view has only a button.
The second view has a map view (MKMapView).
I checked show currentlocation property of this map view
I created outlet and then connect everything by using Builder Interface.
In the dealloc method of the second one, I set nil to delegate of mapview, and then release mapview outlet.
mapView.delegate = nil;
[mapview release];
When I tap the button in view1, view2 will be loaded, and then I tap back button. If I do it normally, everything works well. But If I do it very quickly, repeat many times. The app will be crashed.
If I do not release mapView or I do not check showcurrentlocation property, app works well.
I can't figure out why this happen. Anyone helps me solve this problem. Thanks so much !
My fix in controller class was to ..
(void)dealloc
{
mapView.showUserLocation = NO; // Work around bug in MKMapView
[super dealloc];
}
Try launching your app via Instruments with Zombie instrument added. It should show you overreleased object. You'd also should add the Allocations instrument with VM tracker to see how memory consumption goes over time. This way you'd get your answer very quick.
p.s. more code would help better.