Add UIView to UIWindow causes memory leak - ios

I have a view like this:
#interface MyView ()
#property (nonatomic, strong) UIView* subView;
#end
#implementation MyView
#pragma mark - init && dealloc
-(instancetype)init
{
self = [super init];
if (self){
[self setupSubView];
[self setupAutoLayoutSubView];
}
return self;
}
#pragma mark - setup
-(void)setupSubView
{
_subView = [UIView newAutoLayoutView];
[self addSubview:_subView];
}
-(void)setupAutoLayoutSubView
{
// autolayout: to centralize the subView
// if you don't use PureLayout, you can use the traditional way
[_subView autoCenterInSuperview];
}
#end
When I add this view into Window, the reference counting on this view will be increased to 3.
MyView* v = [[MyView alloc] init];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 1
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubView:v];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 3 INCORRECT
The strange thing is if I comment the "autolayout" part from method: "setupAutoLayoutSubView", the reference counting is correct ( = 2 )
or if I add my view to a normal view, the reference counting is correct as well
MyView* v = [[MyView alloc] init];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 1
UIView* normalView = [[UIView alloc] init];
[normalView addSubView:v];
NSLog(#"reference counting = %ld",CFGetRetainCount((CFTypeRef)v)); //- 2 : CORRECT

Related

Passing variable From UIViewController to UIView

I have tried modify my code many times, but still can't pass variable from UIViewController to UIView, variable always return (null).
// BPGraphView.h
#interface BPGraphView : UIView
#property (nonatomic, retain) NSString *test;
#end
// BPGraphView.m
#import "BPGraphView.h"
#implementation BPGraphView
#synthesize test;
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
NSLog(#"test %#",test);
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"draw %#", test); // always return (null)
if ([test isEqual:#"something"]) {
[self drawOutLine];
}
}
#end
// BloodPressureViewController.m
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"something";
}
In your code:
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
}
The variable graphview is basically destroyed once the viewDidLoad is run to finish. It will never get a chance to run drawRect.
Now the question is how should you define a instance variable of BPGraphView in your UIViewController. The easiest way should be adding a BPGraphView onto your view's xib file and link to an IBOutlet in your UIViewController. In this way, you should be able to assign to test
#IBOutlet BPGraphView graphview;
- (void)viewDidLoad
{
graphview.test = #"dd";
}
Without setting a frame, drawRect will not be called.
BPGraphView * graphview=[[BPGraphView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
graphview.test = #"something";
[self.view addSubview:graphview];
I tried this,it works well,in your code,drawRect is not called
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
[graphview drawRect:CGRectMake(0, 0, 0, 0)];

UIViewController dealloc after init

Why myView is dealloc after init?
MainViewController:
[MOBubbleView hudWithBody:#"123123" bubblePoint:CGPointMake(220, headerMenu.center.y) hidesAfter:2 show:YES];
MOBubbleView.h:
#interface MOBubbleView : UIViewController
#property (nonatomic, assign) float hudHideDelay;
#property (nonatomic, strong) UIColor *itemColor;
+ (MOBubbleView*)hudWithBody:(NSString*)body bubblePoint:(CGPoint)rect hidesAfter:(float)delay show:(BOOL)show;
#end
MOBubbleView.m:
+ (MOBubbleView*)hudWithBody:(NSString*)body bubblePoint:(CGPoint)point hidesAfter:(float)delay show:(BOOL)show {
MOBubbleView *bubble = [[MOBubbleView alloc] init];
///....
if (show) [bubble addToWindow];
return bubble;
}
- (void)addToWindow {
[[[[UIApplication sharedApplication] delegate] window] addSubview:self.view];
}
- (void)loadView {
CGRect bounds = [[UIScreen mainScreen] bounds];
self.view = [[UIView alloc] initWithFrame:bounds];
[self.view setBackgroundColor:[UIColor clearColor]];
/// .. my animation
}
- (void) dealloc {
NSLog(#"Close myView");
}
#end
You would need to retain the view controller that you are returning from your call, so do:
pMyViewController = [myView hudWithBody:#"123123" bubblePoint:CGPointMake(220, headerMenu.center.y) hidesAfter:2 show:YES];
where pMyViewController is declared somewhere that it won't go out of scope - say a global variable for now:
e.g. myView* pMyViewController;
You have added the view part of the view controller onto the window, so that's retained, but the actual controller part has no references holding on to it, so it gets deallocated.

UINavigationController not work with SearchDisplay Controller

In my project using Side menu. It is ready template from gitHub , but the problem is that somtimes searchDisplay controller is work and some times give error , What is actual Problem i am not understad... please help me
side menu template contain this files:
1)JWSlideMenuController
2)JWNavigationController
3)JWSlideMenuViewController
Here the throw error
2013-12-22 13:24:22.671 MyProject[3747:13d03] -[JWNavigationController isNavigationBarHidden]: unrecognized selector sent to instance 0x80c4e60
2013-12-22 13:24:22.681 MyProject[3747:13d03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[JWNavigationController isNavigationBarHidden]: unrecognized selector sent to instance 0x80c4e60'
*** First throw call stack:
(0x1979012 0x1295e7e 0x1a044bd 0x1968bbc 0x196894e 0x4bf326 0x4c0e41 0x410ae4 0x12a9705 0x1dd213 0x29ec7e 0x29e310 0x2aaa31 0x2b3f5f 0x2a21e9 0x2e7d95 0x4ab3c3 0x4ad442 0x4a485a 0x4a399b 0x4a50df 0x4a7d2d 0x4a7cac 0x49fa28 0x20c972 0x20ce53 0x1ead4a 0x1dc698 0x18d4df9 0x18d4ad0 0x18eebf5 0x18ee962 0x191fbb6 0x191ef44 0x191ee1b 0x18d37e3 0x18d3668 0x1d9ffc 0x5da2 0x1f65)
libc++abi.dylib: terminate called throwing an exception
Here is code for JWNavigationController.h and .m file
JWNavigationController.h
#import <UIKit/UIKit.h>
#class JWSlideMenuController;
#interface JWNavigationController : UIViewController <UINavigationBarDelegate>
#property (nonatomic, retain) UINavigationBar *navigationBar;
#property (nonatomic, retain) UIView *contentView;
#property (nonatomic, retain) JWSlideMenuController *slideMenuController;
#property (nonatomic, retain, readonly) UIViewController *rootViewController;
- (id)initWithRootViewController:(UIViewController *)rootViewController;
- (void)pushViewController:(UIViewController *)controller;
- (UIViewController *)popViewController;
#end
JWNavigationController.m file
JWNavigationController.m
// JWNavigationController.m
// JWSlideMenu
//
// Created by Jeremie Weldin on 11/22/11.
// Copyright (c) 2011 Jeremie Weldin. All rights reserved.
//
#import "JWNavigationController.h"
#import "JWSlideMenuViewController.h"
#interface JWNavigationController(Private)
-(UIViewController*)removeTopViewController;
#end
#implementation JWNavigationController
#synthesize navigationBar;
#synthesize contentView;
#synthesize slideMenuController;
#synthesize rootViewController=_rootViewController;
#pragma mark - View lifecycle
- (id)init
{
self = [super init];
if (self) {
CGRect masterRect = [[UIScreen mainScreen] bounds];
CGRect contentFrame = CGRectMake(0.0, 44.0, masterRect.size.width, masterRect.size.height - 44.0);
CGRect navBarFrame = CGRectMake(0.0, 0.0, masterRect.size.width, 44.0);
self.view = [[[UIView alloc] initWithFrame:masterRect] autorelease];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.view.backgroundColor = [UIColor whiteColor];
self.contentView = [[[UIView alloc] initWithFrame:contentFrame] autorelease];
self.contentView.backgroundColor = [UIColor whiteColor];
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.contentView];
self.navigationBar = [[[UINavigationBar alloc] initWithFrame:navBarFrame] autorelease];
self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.navigationBar.delegate = self;
[self.view insertSubview:self.navigationBar aboveSubview:self.contentView];
self.navigationBar.backgroundColor=[UIColor whiteColor];
}
return self;
}
- (id)initWithRootViewController:(JWSlideMenuViewController *)rootViewController
{
self = [self init];
if(self) {
_rootViewController = rootViewController;
UIBarButtonItem *menuButton = [[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"menu_icon_20x20.png"] style:UIBarButtonItemStyleBordered target:self.slideMenuController action:#selector(toggleMenu)] autorelease];
rootViewController.navigationItem.leftBarButtonItem = menuButton;
[self addChildViewController:rootViewController];
[self.contentView addSubview:rootViewController.view];
[self.navigationBar pushNavigationItem:rootViewController.navigationItem animated:YES];
rootViewController.navigationController = self;
}
return self;
}
#pragma mark - UINavigationBarDelegate
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
UIViewController *controller = [self.childViewControllers lastObject];
if (item==controller.navigationItem) //Will now called only if a back button pop happens, not in manual pops
{
[self removeTopViewController];
}
}
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item
{
}
#pragma mark - Stack Interaction
- (void)pushViewController:(JWSlideMenuViewController *)controller
{
[self addChildViewController:controller];
[self.navigationBar pushNavigationItem:controller.navigationItem animated:YES];
controller.navigationController = self;
controller.view.frame = self.contentView.bounds;
if([self.childViewControllers count] == 1)
{
[self.contentView addSubview:controller.view];
}
else
{
UIViewController *previousController = [self.childViewControllers objectAtIndex:[self.childViewControllers count]-2];
[self transitionFromViewController:previousController toViewController:controller duration:0.5 options:UIViewAnimationOptionTransitionNone animations:NULL completion:NULL];
}
}
- (UIViewController *)popViewController
{
//Can use this to pop manually rather than back button alone
UIViewController *controller = [self.childViewControllers lastObject];
UIViewController *previousController = nil;
if([self.childViewControllers count] > 1)
{
previousController = [self.childViewControllers objectAtIndex:[self.childViewControllers count]-2];
previousController.view.frame = self.contentView.bounds;
}
[self transitionFromViewController:controller toViewController:previousController duration:0.3 options:UIViewAnimationOptionTransitionNone animations:NULL completion:NULL];
[controller removeFromParentViewController];
if(self.navigationBar.items.count > self.childViewControllers.count)
[self.navigationBar popNavigationItemAnimated:YES];
return controller;
}
- (void)viewDidUnload
{
_rootViewController = nil;
self.navigationBar = nil;
self.contentView = nil;
self.slideMenuController = nil;
[super viewDidUnload];
}
- (void)dealloc {
[_rootViewController release];
[navigationBar release];
[contentView release];
[super dealloc];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(UIViewController*)removeTopViewController
{
UIViewController *controller = [self.childViewControllers lastObject];
UIViewController *previousController = nil;
if([self.childViewControllers count] > 1)
{
previousController = [self.childViewControllers objectAtIndex:[self.childViewControllers count]-2];
previousController.view.frame = self.contentView.bounds;
}
[self transitionFromViewController:controller toViewController:previousController duration:0.3 options:UIViewAnimationOptionTransitionNone animations:NULL completion:NULL];
[controller removeFromParentViewController];
return controller;
}
#end
If hide this code from JWNavigationController.m so searchDisplayController is working
rootViewController.navigationController = self;
but new problem is UITableVIew didselectrowatindexpath is notworking .. what can i do?
isNavigationBarHidden: is a selector which is only available in classes that derive from UINavigationController. You have to modify this line:
#interface JWNavigationController : UIViewController
with this:
#interface JWNavigationController : UINavigationController
Your JWNavigationController does not inherit from UINavigationController.
But, isNavigationBarHidden is a method implemented by UINavigationController.
Now look at the error (which uses the term 'selector', but that refers to the method): It basically says that the instance of your JWNavigationController does not implement isNavigationBarHidden, and this is the issue.
Could it be that you mistyped, and meant to write:
#interface JWNavigationController : UINavigationController <UINavigationBarDelegate>

IOS 6 subviews overlap toolbar in parent view

I am following Apress Beginning IOS 6 for school where we were asked by the professor to implement a custom algorithm that switches between 3 views (blue, yellow, and green) with each press of the "Next View" button in the toolbar. My approach was to add the subviews at different indices and shuffle the views in the hierarchy in BIDSwitchViewController.m:
#import "BIDSwitchViewController.h"
#import "BIDYellowViewController.h"
#import "BIDBlueViewController.h"
#import "BIDGreenViewController.h"
NSInteger count;
#interface BIDSwitchViewController ()
#end
#implementation BIDSwitchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.yellowViewController = [[BIDYellowViewController alloc] initWithNibName:#"YellowView"
bundle:nil];
self.greenViewController = [[BIDGreenViewController alloc] initWithNibName:#"GreenView"
bundle:nil];
self.blueViewController = [[BIDBlueViewController alloc] initWithNibName:#"BlueView"
bundle:nil];
//the topmost view is the last one in the stack (2)
[self.view insertSubview:self.blueViewController.view atIndex:2];
[self.view insertSubview:self.yellowViewController.view atIndex:1];
[self.view insertSubview:self.greenViewController.view atIndex:0];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.blueViewController = [[BIDBlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:self.blueViewController.view atIndex:0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.;
self.blueViewController = nil;
self.yellowViewController = nil;
self.greenViewController = nil;
}
- (IBAction)switchViews:(id)sender
{
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:
UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];
switch (count)
{
case 0:
[self.view sendSubviewToBack:self.blueViewController.view];
[self.view bringSubviewToFront:self.yellowViewController.view];
[self.view setBackgroundColor:self.yellowViewController.view.backgroundColor];
break;
case 1:
[self.view sendSubviewToBack:self.yellowViewController.view];
[self.view bringSubviewToFront:self.greenViewController.view];
[self.view setBackgroundColor:self.greenViewController.view.backgroundColor];
break;
case 2:
[self.view sendSubviewToBack:self.greenViewController.view];
[self.view bringSubviewToFront:self.blueViewController.view];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
break;
}
if (++count >= 3)
{
count = 0;
}
[UIView commitAnimations];
}
#end
Here is the code in BIDAppDelegate.m where the root view controller is added as an instance of BIDSwitchViewController:
#implementation BIDAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.switchViewController = [[BIDSwitchViewController alloc]
initWithNibName:#"SwitchView" bundle:nil];
UIView *switchView = self.switchViewController.view;
CGRect switchViewFrame = switchView.frame;
switchViewFrame.origin.y += [UIApplication
sharedApplication].statusBarFrame.size.height;
switchView.frame = switchViewFrame;
//[self.window addSubview:switchView];
self.window.rootViewController = self.switchViewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
And the BIDSwitchViewController.h header file:
#import <UIKit/UIKit.h>
#class BIDSwitchViewController;
#interface BIDAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) BIDSwitchViewController *switchViewController;
#end
The app and logic all work as the views switch from blue -> yellow -> green and back to blue again. As shown in the first picture (BIDBlueViewController's BlueView.xib subview of the BIDSubViewController) each of the views overlap the toolbar slightly. I have double and triple checked all of my simulated metrics in IB with the help of one of my classmates:
Am I using poor practice in shuffling the topmost views through the view hierarchy array, instead of removing each view via the book's method of "removeFromParentViewController()," or is their another, hidden cause for the sub views not properly sitting inside / behind the parent view?
Your UIToolbar control's Z-Order on it's view is behind your blue view controller. Since you created it in IB, you can add an IBOutlet for it, and bring it's view to top of the parent view. You're loading the ViewControllers as subviews, so they are all subviews of self.view of your parent.
In your header definition:
IBOutlet *toolBar UIToolbar;
Bring to front in your implementation.
[self.view bringSubviewToFront:toolBar];
You can do this after you've added the blue controller subview or at the end of all your case statements.

Initial view controller in Container View Controller not being shown

I watched the WWDC video on UIViewController Containment and read through this blog post: http://www.cocoanetics.com/2012/04/containing-viewcontrollers/
but I can't get my initial view controller to show. Is there something I am missing? In my ContainerViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_homeViewController = [[HomeViewController alloc] init];
_detailViewController = [[DetailViewController alloc] init];
[self setSubViewControllers:#[_homeViewController, _detailViewController]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (_selectedViewController.parentViewController == self) {
// nothing to do
return;
}
// adjust the frame to fit the container view
_selectedViewController.view.frame = _containerView.bounds;
// make sure that it resizes on rotation automatically
_selectedViewController.view.autoresizingMask = _containerView.autoresizingMask;
// add as child VC
[self addChildViewController:_selectedViewController];
// add it to container view, calls willMoveToParentViewController for us
[_containerView addSubview:_selectedViewController.view];
// notify that move is done
[_selectedViewController didMoveToParentViewController:self];
}
- (void)loadView {
// set up the base view
CGRect frame = [[UIScreen mainScreen] bounds];
UIView *aView = [[UIView alloc] initWithFrame:frame];
aView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
aView.backgroundColor = [UIColor blueColor];
// set up content view
_containerView = [[UIView alloc] initWithFrame:frame];
_containerView.backgroundColor = [UIColor grayColor];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[aView addSubview:_containerView];
self.view = aView;
}
- (void)setSubViewControllers:(NSArray *)subViewControllers {
_subViewControllers = [subViewControllers copy];
if (_selectedViewController) {
// remove previous VC
}
_selectedViewController = _subViewControllers[0];
}
My ContainerViewController is the initial view controller in my storyboard. I see that it shows on the simulator, but the HomeViewController (the first child view controller in my container) does not show.
When I step through the debugger, the subViewControllers property of my ContainerViewController does have the homeViewController and detailViewController in it. The viewDidLoad of HomeViewController also does get called. I just don't see anything on screen except the background color of the ContainerViewController.
Any thoughts? Thanks.
So I'm not the brightest person in the world, but the reason nothing was being shown on the screen was because the nibs were in the storyboard and I needed to do this instead:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
_homeViewController = [storyboard instantiateViewControllerWithIdentifier:#"HomeViewController"];
_detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
Hopefully this helps someone who is also not familiar with Storyboards yet.
You have an NSArray, but you are trying to access it as a C array.
_subViewControllers[0]
should be:
[_subViewControllers objectAtIndex:0];
That being said, you seem to have some code that could be better in other methods. I would personally clean this up a lot and make it much simpler. I would remove loadView and _containerView, and just use self.view as one normally would. For what you are trying to do, there really doesn't even seem a need to track parent and child view controllers. Anyway, this is how I would do it:
#interface ContainerViewController ()
#property (nonatomic, retain) NSArray *subViewControllers;
#end
#implementation ObservationReportViewController {
UIViewController *_selectedViewController;
}
#synthesize subViewControllers = _subViewControllers;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
HomeViewController *homeViewController = [[HomeViewController alloc] init];
homeViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
DetailViewController *detailViewController = [[DetailViewController alloc] init];
detailViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// Retain the view controllers.
self.subViewControllers = #[homeViewController, detailViewController];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setSelectedViewController: [_subViewControllers objectAtIndex:0]];
}
-(void)setSelectedViewController:(UIViewController *)selectedViewController {
if (_selectedViewController != selectedViewController) {
[_selectedViewController.view removeFromSuperview];
_selectedViewController = selectedViewController;
// adjust the frame to fit the container view
[self.view addSubview:_selectedViewController.view];
//_selectedViewController.view.frame = _containerView.bounds;
_selectedViewController.view.frame = self.view.bounds;
}
}
If you set the InitialViewController through the storyboard in a different storyb than the MainStoryboard, then you need to update the project settings to use that new storyboard.
Go to project settings, General and set the Main Interface setting to the new storyboard

Resources