EXC_BAD_ACCESS in [window addSubview:viewcontroller.view] - ios

I have two universal applications... one is giving me an EXC_BAD_ACCESS error when I do this in the app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
ScrollViewController *vc = [[ScrollViewController alloc] init];
[window addSubview:vc.view]; // EXC_BAD_ACCESS here
[window makeKeyAndVisible];
return YES;
}
I do exactly the same (same code, same scroll view controller class) in my other application and get no errors... my scroll view loads fine.
This problem is driving me insane. Here is the ScrollViewController Implementation:
#implementation ScrollViewController
- (void)loadView {
[super loadView];
UIScrollView *s = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
NSArray *a = [[NSBundle mainBundle] loadNibNamed:#"JCEKKeyboard" owner:self options:nil];
UIView *v = [a objectAtIndex:0];
[s setContentSize:CGSizeMake(400, 500)];
[s addSubview:v];
[[self view] addSubview:s];
}

Remember that when you add subviews i.e. [someView addSubview:someOtherView], you are adding potentially a whole graph of subviews (the subviews of someOtherView). If someView is visible it will trigger a render on the new graph of the subview you've added. So if any of the subviews in the new graph are not correctly retained, you can get this error. You must test the entire view hierarchy you're adding.
Use the following gdb commands in your console:
To test if an individual object is valid
po [someView description]
To test the subviews of someView
po [someView subviews]
To test individual subviews (for instance the first subview element 0)
po [[someView subviews] objectAtIndex:0]
Finally to test an entire view hierarchy at once
po [someView recursiveDescription]
These commands, especially recursiveDescription, are also very useful for troubleshooting layout issues.

init shouldn't be creating the view, loadView does that. Calling the view getter (vc.view) when view is nil will cause loadView to be invoked.
Read the documentation for loadView, you are using it incorrectly. You should not call super from loadView and you must set the view property in the view controller. You should not invoke the getter for view [self view] from inside loadView, because the getter calls loadView. You are supposed to create the view in loadView and set the view property.
Something like this:
- (void)loadView {
NSArray *a = [[NSBundle mainBundle] loadNibNamed:#"JCEKKeyboard" owner:self options:nil];
UIView *v = [a objectAtIndex:0];
CGRect frame = [UIScreen mainscreen].applicationFrame;
UIScrollView *s = [[UIScrollView alloc] initWithFrame: frame];
[s setContentSize:CGSizeMake(400, 500)];
[s addSubview:v];
self.view = s;
}

Related

UIVisualEffectView built in storyboard is opaque

I've built an UIView in storyboard that is composed like this:
UIView (called _view)
UIVisualEffetView (dark)
UIVIew
Button
I've done like this for the ability to reuse it between several Navigation Controller. So for this I've set everything and I can show my view in my main controller called "ViewController" that is embedded in a navigation controller. But the issue is that the blur is opaque, for example the blue navigation bar of my view controller is not visible under the UIVIsualAffectView !
Here is the code I use for setting up the custom view :
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
_view.backgroundColor = [UIColor clearColor];
UIView *rootView = [[[NSBundle mainBundle] loadNibNamed:#"SideMenuView" owner:self options:nil] objectAtIndex:0];
CGRect newFrame = rootView.frame;
newFrame.size.width = [[UIScreen mainScreen] bounds].size.width / 2;
newFrame.size.height = [[UIScreen mainScreen] bounds].size.height;
[rootView setFrame:newFrame];
UIWindow* currentWindow = [UIApplication sharedApplication].keyWindow;
currentWindow.backgroundColor = [UIColor clearColor];
currentWindow.windowLevel = UIWindowLevelNormal;
[currentWindow addSubview:rootView];
}
And here is the code I use to call the view from my main controller:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.leftBarButton setTarget:self];
[self.leftBarButton setAction: #selector(test)];
}
- (void)test {
SideMenuView *test = [[SideMenuView alloc] init];
[self.view addSubview:test];
test.translatesAutoresizingMaskIntoConstraints = NO;
}
I've also tried to force the property "setOpaque" to No in my SideMenuView.m but without effect
Here is a screenshot of what it do:
Do you know guys what is wrong with my code ?
I've also added a button in the middle of the screen with a blue background but it's not visible under the blur so the issue is not navigation bar specific
Thanks in advance for your help !
I've resolved my issue by simply creating an UIView with clear background.
What I do for adding the blur effect is to create the UIVisualEffectView programmatically (doing this way it work perfectly !).
After that I'm adding my UIView as a subview of my UIVisualEffectView and voila :)

Load View Controller from storyboard with a particular size

I want to know if it is possible to load/present modally a UIViewController with a "free form" size like the image below
I want to load the red one allways on top (allways visible) and interact with the ViewController below (blue one) at the same time.
EDIT: what i want is to show and hide FROM APP DELEGATE a small view controller to controll the music in my app.
I was trying with a xib file with a custom UIViewController as the file owner (see image below). The xib is being showed right the way i want, but the UIViewController associated to it, id doesn't load and i don't know why...
So i thought to do it with a UIViewController free formed but im not making it and i don't know wich is the best way
EDIT2:
With the xib file way i did the next:
In AppDelegate i have a function showPlayer that is called with local notification to show the xib
-(void)showPlayer:(int)tag andObjetoCancion:(NSArray *)objetoCancion
{
ZocaloPlayerViewController *vc = [[ZocaloPlayerViewController alloc] initWithNibName:#"ZocaloPlayerView" bundle:nil];
[vc setObjectoCancion:[objetoCancion mutableCopy]];
if (globals.isPlaying) {
[vc setCambiar:YES];
}else{
[vc setCambiar:NO];
}
//here playerView is a UIView initiated in didFinishLaunchingWithOptions
if (playerView == nil) {
playerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.window.frame.size.height, self.window.frame.size.width, altoPlayer)];
}
//keyWindow is UIWindow
//NSLog(#"Abro view %ld", (long)viewPlayer.tag);
keyWindow = [[[UIApplication sharedApplication] delegate] window];
[keyWindow addSubview:playerView];
[keyWindow bringSubviewToFront:playerView];
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"ZocaloPlayerView" owner:vc options:nil];
mainView = [subviewArray objectAtIndex:0];
mainView.translatesAutoresizingMaskIntoConstraints = NO; //This part hung me up
[playerView addSubview:mainView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[playerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-0-[mainView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(mainView)]];
[playerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-0-[mainView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(mainView)]];
[UIView animateWithDuration:0.3 animations:^{
playerView.center = CGPointMake(self.window.frame.size.width / 2, self.window.frame.size.height - mitadAltoPlayer);
[self.window layoutIfNeeded];
}];
}
ZocaloPlayerViewController *vc is being instantiated but the viewDidLoad function inside it is not being called.... That is my first problem...
With the UIViewController free formed way i don't know how to do it...
Ok, i solved my problem with this post Loaded nib but the view outlet was not set - new to InterfaceBuilder
(i was missing the UIView outlet)
and i changed my code in the showPlayer function to this:
-(void)mostrarPlayer:(int)tag andObjetoCancion:(NSArray *)objetoCancion
{
ZocaloPlayerViewController *vc = [[ZocaloPlayerViewController alloc] initWithNibName:#"ZocaloPlayerView" bundle:nil];
[vc setObjectoCancion:[objetoCancion mutableCopy]];
if (globals.isPlaying) {
[vc setCambiar:YES];
}else{
[vc setCambiar:NO];
}
if (playerView == nil) {
playerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.window.frame.size.height, self.window.frame.size.width, altoPlayer)];
}
//NSLog(#"Abro view %ld", (long)viewPlayer.tag);
keyWindow = [[[UIApplication sharedApplication] delegate] window];
[keyWindow addSubview:playerView];
[keyWindow bringSubviewToFront:playerView];
[playerView addSubview:vc.view];
[UIView animateWithDuration:0.3 animations:^{
playerView.center = CGPointMake(self.window.frame.size.width / 2, self.window.frame.size.height - mitadAltoPlayer);
[self.window layoutIfNeeded];
}];
}

How to have images that pinch-zoom in a paginating scroll view in iOS?

I'm trying to use a scroll view to have pagination with pages of subviews that are images that can be pinched zoomed on iOS. The pagination works, but as soon as an image is pinch-zoomed, the app crashes with EXEC_BAD_ACCESS(code=1,address=...)
I'm aware that it's a bit odd to swipe a zoomed image to pan the image and also swipe to paginate, but in the real app, the pagination will be done with a page control. Also I think it could work like the preview app. If an image is zoomed, panning will go down to the bottom of the image and then after that is reached, it goes to the next image.
Is this possible?
Here's an example:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ScrollerViewController *viewController = [[ScrollerViewController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
ScrollerViewController.m - the outer pagination view controller
- (void)viewDidLoad {
[super viewDidLoad];
// outer scroll view for paging with two pages
CGRect frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
UIScrollView *pagingScroller = [[UIScrollView alloc] initWithFrame:frame];
pagingScroller.pagingEnabled = YES;
pagingScroller.scrollsToTop = NO;
pagingScroller.userInteractionEnabled = YES;
pagingScroller.contentSize = CGSizeMake(self.view.bounds.size.width*2,self.view.bounds.size.height);
// first page
ImageViewController *page1 = [[ImageViewController alloc] init];
page1.filename = #"cat.jpg";
page1.view.frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
[pagingScroller addSubview:page1.view];
// second page
ImageViewController *page2 = [[ImageViewController alloc] init];
page2.filename = #"dog.jpg";
page2.view.frame = CGRectMake(self.view.bounds.size.width,0,self.view.bounds.size.width,self.view.bounds.size.height);
[pagingScroller addSubview:page2.view];
self.view = pagingScroller;
}
ImageViewController.m - the pinch-zoom image
- (void)viewDidLoad {
[super viewDidLoad];
// scroll view for pinch zooming
CGRect frame = CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height);
UIScrollView *zoomScroller = [[UIScrollView alloc] initWithFrame:frame];
zoomScroller.minimumZoomScale = 1.0;
zoomScroller.maximumZoomScale = 5.0;
zoomScroller.userInteractionEnabled = YES;
zoomScroller.delegate = self;
imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.userInteractionEnabled = YES;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:filename];
[zoomScroller addSubview:imageView];
self.view = zoomScroller;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imageView;
}
The full project is at https://github.com/tomkincaid/ZoomScrollTest
I can test that the pinch zoom works by changing
ScrollerViewController *viewController = [[ScrollerViewController alloc] init];
to
ImageViewController *viewController = [[ImageViewController alloc] init];
viewController.filename = #"cat.jpg";
Its been quite a while that you posted your question. I bet you fixed it already yourself but I want to make sure other people can use your code.
However I downloaded your small GitHub project and found that you get the crash because you don't retain the ImageViewController's page1 and page2 in [ScrollerViewController viewDidLoad]. The views them selfs don't retain their controllers therefor the controllers get released after viewDidLoad in your case. Then when you pinch on the image scroll view it calls for its delegate but it is already deallocated.
To fix this I added two ImageViewController properties to the ScrollerViewController class and stored the controller objects there.
#interface ScrollerViewController ()
#property (strong) ImageViewController *page1;
#property (strong) ImageViewController *page2;
#end
In [ScrollerViewController viewDidLoad] I added at the end:
self.page1 = page1;
self.page2 = page2;
I hope that someone may find this information useful. Maybe you want to update your GitHub project so that it will compile and run.

loadNibNamed vs. initWithFrame dilemma for setting frame's height and width

I created a UIView subclass associated with .xib file. This UIView subclass is to be used in a UIViewController. In the controller, I think there are two options how we instantiate the UIView subclass:
MyUIView *myView=[[MyUIView alloc] initWithFrame:aRect];
and
MyUIView *myView = [[[NSBundle mainBundle] loadNibNamed:#"MyUIView"
owner:self
options:nil] lastObject];
I prefer the first approach or its variants that allow me to perform custom initialization. The only problem is that I have to specify a frame's rect, which was already specified in .xib (I mean frame's height and width of MyUIView). Yes, I can hardcode it again in aRect, but this is tedious to maintain (e.g., when I change position of UIs in .xib, I have to update aRect, too).
So the second approach should come into mind as the frame rect is automatically set. The remaining problem is I cannot customize the initializer (say, I want to pass extra parameters during initialization).
What's your preference? Which one is better in your opinion?
EDIT1:
Inspired by sergio's answer, I came out with this workaround:
// In MyViewController.m
MyUIView *myView=[[MyUIView alloc] initWithFrame:CGRectMake(x, y, 0.0, 0.0)];
// In MyView.m
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self = [[[NSBundle mainBundle] loadNibNamed:#"UnmovableTagView"
owner:self
options:nil] lastObject];
[self setFrame:CGRectMake(frame.origin.x,
frame.origin.y,
[self frame].size.width,
[self frame].size.height)];
// frame's width and height already determined after
// loadNibNamed was called
...
}
return self;
}
Have you tried using:
MyUIView *myView=[[MyUIView alloc] initWithFrame:CGRectZero];
I don't know if it works in your case (loading the view from a nib), but I use it successfully when I create my custom view programmatically.
You can give it a try.
EDIT:
you could define your own init method for your view:
-(id)initWithPosition:(CGPoint)pos;
and then call:
-(id)initWithPosition:(CGPoint)pos {
if (self = [super initWithFrame:{pos,{0,0}}]) {
self = [[[NSBundle mainBundle] loadNibNamed:#"UnmovableTagView"
owner:self
options:nil] lastObject];
[self setFrame:CGRectMake(frame.origin.x,
frame.origin.y,
[self frame].size.width,
[self frame].size.height)];
// frame's width and height already determined
// after loadNibNamed was called
}

Subview common to multiple view controllers is unresponsive

I am using a UIView to show busy/processing clue to User and components in loading view are not responding to user touches. Let me elaborate my problem.
I have around 3 UIViewControllers and based on situation one View from VieControllers would be displayed. I use following code to change current view
UIView *currentView = self.view;
UIView *theWindow = [currentView superview];
//Some other code
[currentView removeFromSuperview];
[self.view setFrame: [tileCountViewController.view bounds]];
[theWindow addSubview:tileCountViewController.view];
[theWindow setFrame:[tileCountViewController.view bounds]];
[theWindow setFrame: [[UIScreen mainScreen] bounds]];
I then have a Loading view which generally used to show busy/processing kinda of UI to User. I created Loading view in following way from nib file
- (id)init {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
NSString *nibName = #"LoadingViewController";
if(UIInterfaceOrientationIsLandscape(orientation)) {
nibName = #"LoadingView-landscape";
}
else {
nibName = #"LoadingView";
}
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
UIView *portraitView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self = [super initWithFrame:portraitView.frame];
[self addSubview:portraitView];
[self bringSubviewToFront:portraitView];
return self;
}
In app delegate, I initialize loading view and add it to Main window
if(!loadingViewIndicator){
loadingViewIndicator = [[LoadingView alloc]init];
}
[[[UIApplication sharedApplication]keyWindow]addSubview:loadingViewIndicator];
loadingViewIndicator.hidden = YES;
Whenever I need to show loading/busy view, I simply load it like this
[loadingViewIndicator performSelectorOnMainThread:#selector(startLoadingViewIndicator:) withObject:startMsg waitUntilDone:NO];
- (void)startLoadingViewIndicator:(NSString *)startMsg{
self.hidden = NO;
[self.superview bringSubviewToFront:self];
[self.activityIndicator startAnimating];
[self.loadingMessage setText:startMsg];
self.loadingProgressView.progress = 0.0f;
[self refreshPosition];
}
Everything works fine but components in Loading view are not responding to touches. Any clue what is going wrong?
I just realized that problem is due to threading issue. I show this subview when User starts downloading any file. My earlier code was to directly start download
urlconnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately: YES];
I am now calling this method from another method using background thread as below
[self performSelectorInBackground:#selector(startDownload) withObject:nil];
And then I am adding runloop in download like
urlconnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately: YES];
CFRunLoopRun();
This works fine and my subview is responsive. It really took lots of time. The problem is there is clear code example to help to run download in background thread and at the same time make UI responsive.

Resources