UISlider subclass causing UI hang in only one UIView of universal app - iphone

I have been working on the front-end to a universal iOS app, setting up separate iPhone and iPad view controllers as subclasses of a XIB-less view controller (as described in http://www.kotancode.com/2011/04/05/ios-universal-apps/). Each subclassed view controller contains a device-specific XIB, with each XIB containing portrait and landscape UIViews which are switched upon orientation change (I followed http://aseriesoftubes.com/articles/ipad-development-101-orientation/ for this). The controls common to all four UIViews are synchronised with an IBAction method linked through IBOutletCollections.
Just today I have created a UISlider subclass for my own slider design, which appears to work fine, apart from one large issue: when testing with the iPhone in the simulator starting in portrait orientation the slider works fine; when switched to landscape it is also fine; however when I switch back to portrait the slider works for up to two operations and then freezes, taking some other controls of the view with it; stranger still, if I switch back to landscape the slider works fine! This happens only with the iPhone simulator, not the iPad.
I have tried switching IB's 'Continuous' option on and off, and tried both the 'Touch Up Inside' and 'Value Changed' connections from my slider's Sent Events, with no change (as it will be for a volume control I will need 'Value Changed' to work). My slider's implementation code is thus:
#implementation VolumeSlider
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super initWithCoder: decoder])) // Double paretheses to silence warning
{
// Instantiate slider images
UIImage *thumbImage = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"volume thumb" ofType: #"png"]];
UIImage *trackImage = [[UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"volume track" ofType: #"png"]] stretchableImageWithLeftCapWidth: 20 topCapHeight: 0];
// Set control elements with images
[self setThumbImage: thumbImage forState: UIControlStateNormal];
[self setMinimumTrackImage: trackImage forState: UIControlStateNormal];
[self setMaximumTrackImage: trackImage forState: UIControlStateNormal];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
The two action methods that the sliders trigger are:
- (IBAction)volumeChange:(id)sender
{
NSLog(#"VOLUME slider changing");
// VOLUME slider action
}
- (IBAction)volumeChangeFinish:(id)sender
{
NSLog(#"VOLUME change finished");
VolumeSlider *incomingVolume = sender;
for(VolumeSlider *volumeSlider in self.volumeSliders)
{
volumeSlider.value = incomingVolume.value;
}
}
(volumeChange will use the slider value to change volume in real time, volumeChangeFinished will update the other sliders in the view on touch inside up.)
This one has me stumped – it feels like a memory issue but I can't figure out exactly where. Does anyone have an idea what could be causing this?
(Mac OS 10.6.8, Xcode 4.0.2, building for iOS 4.3)
UPDATE ---
I have tried instantiating a new, stock UISlider into the iPhone view and this suffers the same problem, without having been connected up to anything. This points towards an issue with the view controller, or even some bug.

Although I have no definitive reason for the issue, I have managed to fix this. Basically I created a new UIView for the iPhone portrait orientation in Interface Builder, rebuilt the interface and connected it up in place of the dodgy one and the issue was gone. I don't know if I could have had a corrupted view in the XIB, that's the only answer I can proffer.

Related

Issues setting up a back ground image and with UIImageView

On my iPhone app, I simply want to set a particular background image, which depends on whether it's an iPhone 5 or not.
So, I tried two approaches:
A) Using
self.view.backgroundColor = [UIColor colorWithPatternImage:backGroundimage];
B) Creating an UIImageView and setting up the image there. Code:
UIImageView *backgroundImageView = [[UIImageView alloc]initWithFrame:screenBounds];
[backgroundImageView setImage:[UIImage imageNamed:backGroundImage]];
[self.view addSubview:backgroundImageView];
But I am having issues with both of them:
Issues with Step A:
When I set the image through that way, I have to deal with the image scaling issues for different sizes of the screen. I use the following code to do the scalling:
UIGraphicsBeginImageContext(screenBounds.size);
[[UIImage imageNamed:backGroundImage] drawInRect:screenBounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.view.backgroundColor = [UIColor colorWithPatternImage:image];
Another issue from Step A is that the image appears quite blurry. It doesn't have the same sharpness to it.
Issues with Step B:
With this, the image looks really crisp and sharp - just the way it should look.
But when I switch to another view using the following code, strangely enough the UIImageView backgroundImageView still appears on the second one. The code I use to switch views is:
[self presentViewController:secondViewController animated:YES completion:nil];
I even tried [backgroundImageView removeFromSuperview], but that doesn't solve anything either.
So what am I doing wrong? And how can I set up a picture as my background which is dependent on the size of the iphone?
Plan B is a good plan. Presenting another view controller should and will definitely hide your image view. If it isn't happening, then it's a problem with the creation of secondViewController, unrelated to the background image on the presenting VC.
Before presenting secondViewController, log it:
NSLog(#"presenting %#", secondViewController);
I'll bet a dollar that it's nil. If I'm right, let's have a look at how you initialize secondViewController. That's the problem, unrelated to the background image view.
Okay, I finally fixed this issue, although the cause of this issue is still puzzling to me.
To fix this, I had to create an IBOutlet property for UIImageView and hook it up on the XIB file.
The reason I was programmatically creating the UIImageView is because the size of the UIImageView depends on what size iPhone they are using. But for the IBOutlet (let's call it as UIImageViewOutlet, I simply used [self.UIImageViewOutlet setFrame:] to get the size and location that I wanted.
I also discovered that one of the buttons that I was programmatically creating, was still visible in the secondViewController. I ended up creating an Outlet on the XIB file for that one as well and used setFrame on it to position it properly.
If anyone who knows the reason of this problem, I will be very grateful.

What exactly is done to a UIView on interface rotation?

could someone explain what iOS does on rotation of the interface. I´ve got a layout problem with one View that is gone after rotating the iPhone. Seems that the View got set a new frame, bounds or whatever, don´t know. Anyhow after the interface was rotated once the layoutproblem is gone forever. So something must be set to the view at the time the interface rotates.
I´m loading the View from a NIB file and show it with a navigationcontroller:
BirthdayReminderWidgetConfigViewController *vc = [self.storyboard
instantiateViewControllerWithIdentifier:#"BirthdayConfigController"];
self.navigationController pushViewController:vc animated:true];
Maybe there is some setting I have to do in order to show up the view correct without rotating the interface.
I´ve got a layout problem with a view that is loaded from a nib file as follows:
The project contains a MainStoryboard. Within that, I load a view from a nib file.
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil];
self.configViewController = [xib objectAtIndex:0];
The storyboard has a navigation controller and the loaded view is shown like this:
if (currentWidgetConfigViewController != nil)
{
[self.navigationController pushViewController:currentWidgetConfigViewController animated:true];
}
So in my opinion nothing wrong? (First question)
But now the problem.
What I designed is that:
What iOS does is the following
The controls are not arranged well.
And besides that with a button on the new view I open up a PeoplePicker with that code:
[self presentViewController:self.picker animated:YES completion:nil];
After closing the People picker with [self.picker dismissViewControllerAnimated:true completion:nil]; I get this result:
So what is going wrong here?
Override -setFrame: and -setBounds: in your view to see what happens:
- (void) setFrame: (CGRect) newFrame
{
NSLog(#"New frame: %#", NSStringFromCGRect(newFrame));
[super setFrame:newFrame];
}
Also, the transform usually changes during a rotation. Orientation Zoo might help, it’s a sample Xcode project showing what happens in various rotation use cases. Didn’t touch it for a long time, though, so I don’t know if it still works.

MPMoviePlayerViewController, remove quicktime symbol/add background image?

I have an MPMoviePlayerViewController that plays audio. I would like to remove the Quicktime logo and/or add a custom background image to the player but keep the playback controls. I swear I have done this before prior to iOS 5 but I can't re-figure it out! Things I have tried:
-Adding a subview to MPMoviePlayerViewController. Puts the subview OVER the playback controls, not good.
-setBackgroundColor of the moviePlayer with a patternimage. Doesn't do anything.
-setView on the entire MPMoviePlayerViewController. As expected, the playback controls and navigation bar are gone. I suppose this could work but I'd need to recreate all the playback controls manually, but I'd really rather not.
Can anyone shed some light?
Check out the MPMoviePlayerController property backgroundView.
From the MPMoviePlayerController Class Reference;
backgroundView
A customizable view that is displayed behind the movie
content. (read-only)
#property (nonatomic, readonly) UIView *backgroundView
Discussion This
view provides the backing content, on top of which the movie content
is displayed. You can add subviews to the background view if you want
to display custom background content.
This view is part of the view hierarchy returned by the view property.
Availability Available in iOS 3.2 and later. Declared In
MPMoviePlayerController.h
Try something like this (assuming that your instance of the MPMoviePlayerController is called moviePlayerController):
patternView = [[UIView alloc] initWithFrame:self.view.bounds];
patternView.backgroundColor = [UIColor redColor];
[moviePlayerController.backgroundView addSubview:patternView];
[patternView release];
Till's answer is correct for iPhoneOS 3. This has changed in iOS 4 and 5.
Apple has now added a readonly property to MPMoviePlayerViewController exposing a MPMovieController. The MPMovieController has a backgroundView that should be used.
Now you will want to do something like this:
patternView = [[UIView alloc] initWithFrame:self.view.bounds];
patternView.backgroundColor = [UIColor redColor];
[moviePlayerController.moviePlayer.backgroundView addSubview:patternView];
[patternView release];

Two NIBs, same objects (iPhone)

So I am setting up my application rotation, and I have been setting up all of the button translations manually using the following method:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)x
duration:(NSTimeInterval)duration{
CGRect bounds = [[self view] bounds];
if (UIInterfaceOrientationIsPortrait(x)) {
[anObject setFrame: CGRectMake(0, 0, bounds.size.width, 194)];
}
else {
[anObject setFrame: CGRectMake(0, 0, bounds.size.width, 110)];
}
}
I'm setting up each object's position in both orientations. It seems like a horrible way of accomplishing my goal..
So now what I'd like to do is make a copy of my main nib, and set up all of the buttons' positions in landscape on the new nib. Then when the device rotates, call the corresponding nib. Is that possible/feasible/an alright way to do it?
How would I call the nib for rotation? Can I use the exact same objects with the same IBconnections?
Is there a better way?
I've used a couple of different approaches depending on the behavior I needed. See which works best for you.
Find a set of rules which allow you to support both orientations using only autoresizingMask and autoresizesSubviews.
Find a set of rules for the parent view's -layoutSubviews such that the subviews can be rearranged relative to the parent view's bounds. That's more work than autoresizing but better than two hardcoded positions for every subview. This often requires the creation of a deeper view hierarchy to nest several views with different -layoutSubviews behaviors to get appropriate positioning of and spacing around the visible subviews.
Have two subviews, one for each orientation. Hide one when you rotate. This allows custom transitions between the subviews which you won't get if you load a new nib and replace the root view. If you load each subview from it's own nib you can then unload whichever one is no longer visible to reduce memory use. You then need to make sure both views display the same state, trigger the same IBActions, and have extra IBOutlet references (or carefully capture state from your current IBOutlets before rebinding them by loading a new subview from a nib) so you can set attributes of equivalent views in each rotation.
Combine options 2 and 3 above to load different views with different layout behaviors for each rotation and shuffle subviews back and forth between them.
This is a much easier way - two NIBs, using Apple's naming convention. I suspect that in iOS 6 or 7, Apple might add this as a "feature". I'm using it in my apps and it works perfectly:
triggers on WILL rotate (waits until the rotate anim is about to start)
uses the Apple naming convention for landscape/portrait files (Default.png is Default-landscape.png if you want Apple to auto-load a landscape version)
reloads the new NIB
which resets the self.view - this will AUTOMATICALLY update the display
and then it calls viewDidLoad (Apple will NOT call this for you, if you manually reload a NIB)
(NB stackoverflow.com requires this sentence here - there's a bug in the code formatter)
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )
{
[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:#"%#-landscape", NSStringFromClass([self class])] owner:self options:nil];
[self viewDidLoad];
}
else
{
[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:#"%#", NSStringFromClass([self class])] owner:self options:nil];
[self viewDidLoad];
}
}

Customized UINavigationbar in Universal App

I have a Universal App in which I customize my UINavigationBar.
In my iPhone AppDelegate I use this to achieve it:
#implementation UINavigationBar (CustomImage)
static NSMutableDictionary *navigationBarImages = NULL;
- (void)initImageDictionary
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawing navbar2");
UIImage *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject: self]];
if (imageName==nil) {
imageName=[UIImage imageNamed:#"bg_titleBar.png"];
UIImage *image = imageName;
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
}
- (void)setMyImage:(UIImage*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject: self]];
[self setNeedsDisplay];
}
#end
Now my questions: why does this code get called, although I start the iPad simulator?
And more important it seems to corrupt the UIPopoverController because it looks like this:
http://awesome-apps.com/pic/ok.png
While it should look somehow like this:
http://awesome-apps.com/pic/nok.png
Besides it corrupts more in my App, but this should be it for starters :)
Can anyone help me with this? Have you ever had a similar experience?
So ignore the fact that it gets called when you run in the iPad simulator, because as you'll see in a minute even if you used two different categories (one for iPhone, one for iPad) you'd still have this problem.
Here's why:
You are using a category to override the UINavigationBar behaviour. I assume you know what that means - crucially any and all navigation bars in your app will use your supplied methods in the category.
This can cause problems if you're using standard apple elements that use UINavigationBars - the exact thing you're seeing in the popover controller. What's happening is the UIPopoverController uses a UINavigationBar. But because you've defined a category, the app assumes you want the popover navbar to use that category as well.
So that's why you're seeing your weird behaviour in your pop-over controller.
As long as you use categories you'll have this problem, because you can't selectively tell the system which bars should use your category.
I'd suggest you tell us exactly you're trying to customise in the navbar, because there are other ways to achieve customisation outside of categories.