UIModalTransitionStylePartialCurl acting strange with subclassed elements in iOS 5 - iphone

I have a UIModalTransitionStylePartialCurl which is acting funky. My views UILabel and UIButton animate as if the labels frame is being changed all the time.
In iOS 4 or 6 it works statically. I've made a video of the bug on youtube.
I found an answer to this problem, but I don't quite understand the implementation. I alloc and init all of my elements in viewDidLoad, so where exactly is [self layoutIfNeeded]; going to help?
My code, if relevant is as follows (refactored):
- (void)viewDidLoad
{
[super viewDidLoad];
// Label
textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 120, 280, 40)];
textLabel.text = #"Change password";
[self.view addSubview: textLabel];
// old password
oldPassword = [[UITextField alloc] initWithFrame: CGRectMake(20, 180, 280, 40)];
oldPassword.secureTextEntry = YES;
oldPassword.placeholder = #"Current Password";
oldPassword.returnKeyType = UIReturnKeyNext;
oldPassword.autocapitalizationType = UITextAutocapitalizationTypeNone;
oldPassword.delegate = self;
[self.view addSubview: oldPassword];
// New password
CGRect chosenPasswordFrame = oldPassword.frame;
chosenPasswordFrame.origin.y += 40;
chosenPassword = [[CustomTextField alloc] initWithFrame: chosenPasswordFrame];
chosenPassword.secureTextEntry = YES;
chosenPassword.placeholder = #"New Password";
chosenPassword.returnKeyType = UIReturnKeyGo;
chosenPassword.autocapitalizationType = UITextAutocapitalizationTypeNone;
chosenPassword.delegate = self;
[self.view addSubview: chosenPassword];
// Submit button
submitButton = [[UIButton alloc] initWithFrame:CGRectMake(20, chosenPasswordFrame.origin.y + chosenPasswordFrame.size.height + 20, self.view.frame.size.width - 40, 45)];
[submitButton setTitle:#"Submit" forState:UIControlStateNormal];
[submitButton addTarget:self action:#selector(changePassword) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: submitButton];
self.view.backgroundColor = [UIColor colorWithPatternImage:[[AppTheme sharedTheme] changePasswordBackgroundImage]];
}

It looks like the setting of your view's frames is getting caught in the CATransaction being created by the partial page curl animation and being animated. Since the frames should never have been CGRectZero at least in the code you posted, this does seem to be a bug in iOS 5. Especially based on the number of up-votes that the answer you linked has received. And that answer describes the problem well.
Essentially, if you force the new view to lay itself out immediately by calling [self layoutIfNeeded] it forces the view system to realize that these are the current frame values not the ones to be animated to. And then even if this bug still tries to animate it's a non-op. Animating from frame A to frame A is at the very worst imperceptible. And since the "buggy" animation would finish at the same time as the curl animation it really (in practice) doesn't matter to you. With the exception of an inexplicable call to layoutIfNeeded in viewDidLoad.

Related

Very odd UIView position issue

I'm having a strange issue when it comes to adding content to a UIScrollView.
Below are the results for the same method getting called. The one on the left is the result of the call from viewDidLoad. The one one the right is called from a custom method which fires when the label is touched.
The code is pretty straight forward:
CGRect scrollRect = CGRectMake(0, 64, 320, [[UIScreen mainScreen] bounds].size.height - 49);
_containerView = [[UIScrollView alloc] initWithFrame:scrollRect];
_containerView.backgroundColor = [UIColor clearColor];
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 300, 40)];
myLabel.text = #"my label";
myLabel.textColor = [super colorFromHexString:0x472C37];
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
y_offset += myLabel.frame.size.height;
[_containerView addSubview:myLabel];
[self.view addSubView:_containerView];
I've checked the parent (self.view) and its coordinates are always 0,0. Really stumped by this...
In viewDidLoad your subViews components are not totally initialized yet, so if you are using the frame of some of them you will get an undesired result. viewDidLayoutSubviews is probably what you are looking for. This is the method where all the subviews frames are completly initialized.
Try to call your code inside this method and you should get the same result as the one when clicking in the button.
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self setupView];
}

Programmatically add UILabel to toolbar

I am trying to add a UILabel programmatically into my UIToolBar but it dose not seem to be appearing. This is what I am doing with my code.
- (void) viewWillAppear:(BOOL)animated
{
// Create custom toolbar at top of screen under navigation controller
[matchingSeriesInfoToolBar setFrame:CGRectMake(0, 60, 320, 30)];
matchingSeriesInfoToolBar = [UIToolbar new];
[matchingSeriesInfoToolBar sizeToFit];
CGFloat toolbarHeight = 30;
CGRect mainViewBounds = [[UIScreen mainScreen] applicationFrame];
[matchingSeriesInfoToolBar setFrame:CGRectMake(CGRectGetMinX(mainViewBounds), 0, CGRectGetWidth(mainViewBounds), toolbarHeight)];
matchingSeriesInfoToolBar.tintColor = [UIColor darkGrayColor];
[self.view addSubview:matchingSeriesInfoToolBar];
// Create size of uitableview (to fit toolbar.
[matchingSeriesTableView setFrame:CGRectMake(0, 30, self.view.frame.size.width, self.view.frame.size.height - 30)];
[self.view addSubview:matchingSeriesTableView];
// Ad UILabel to the toolbar
UIBarButtonItem *textFieldItem = [[UIBarButtonItem alloc] initWithCustomView:manufSelectionLabel];
matchingSeriesInfoToolBar.items = [NSArray arrayWithObject:textFieldItem];
manufSelectionLabel.text = #"Hello World!";
[super viewWillAppear:animated];
}
So pretty much I have created a custom toolbar which I have changed the usual location from the bottom of the screen, to appear under the UINavigationController, this is also added to the view like this so it animated properly in the view transitions..
After which I create the size of the tableview so that it appears after the custom toolbar..
then from there I am trying to add a UILabel to the toolbar.. but for some reason its not working out.
Any help would be greatly appreciated.
You must actually create the label somewhere. This code works just fine.
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[self.view addSubview:toolbar];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 44)];
label.backgroundColor = [UIColor clearColor];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:label];
toolbar.items = [NSArray arrayWithObject:item];
label.text = #"Hello World";
In the code you posted you don't ever make a UILabel. Your comment says Ad UILabel to the toolbar but you then proceed to make a UIBarButtonItem with a custom view manufSectionLabel.
Where is the code which creates manufSectionLabel?
PS This line does nothing :
[matchingSeriesInfoToolBar setFrame:CGRectMake(0, 60, 320, 30)];
because at that point matchingSeriesInfoToolbar is nil - you haven't made it yet!

How add UIView to a View as a Subview (for giving user feedback)

I'm creating a rate-a-fish application. When the user rates the fish, I want to display a little box which says "Average rating: * " with stars.
I have achieved something similar in cells for a table, but can't seem to get it to work in a normal view (I run the following code and nothing appears in the view):
-(void)displayAvg:(NSInteger)avg{
UILabel *text = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 90, 20)];
text.text = #"Average Rating:";
UIView *wrapper = [[UIView alloc]initWithFrame:CGRectMake(100, 200, 100, 30)];
wrapper.layer.cornerRadius = 8;
wrapper.backgroundColor = [UIColor whiteColor];
[wrapper addSubview:text];
[text release];
UIImageView *star;
NSInteger ratingI = avg;
for(int i = 0; i<ratingI; i++) {
star = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"little_star.png"]];
star.frame = CGRectMake(50 + (i * 16), 0, 16, 16);
[wrapper addSubview:star];
[star release];
}
[self.view addSubview:wrapper];
[self.view bringSubviewToFront:wrapper];
NSLog(#"Sleeping...");
[NSThread sleepForTimeInterval:3];
[wrapper removeFromSuperview];
[wrapper release];
}
I tried this code The problem is with the following line
[NSThread sleepForTimeInterval:3];
So instead of this line, you write a new method and call it with 'wrapper' as argument to remove the wrapper view.
[self performSelector:#selector(removeWrapper:) withObject:wrapper afterDelay:3];
implement the method
-(void)removeWrapper:(id)sender
{
UIView *wrapper = sender;
[wrapper removeFromSuperview];
[wrapper release];
}
This isn't answer to your question but I think it will be better to add timer that will fire in 3 seconds and then remove the window; you will not block the thread this way; Have you tried to add just simple sub view at first, i.e. wrapper with no other code? does it appear then? Or actually as I rethink the problem it might be the answer to your question - blocking thread this way might be the cause the window never get displayed. So far I know UI will be updated after thread returns to the run-loop.

Best Practice to Float a View over Another View?

I have a 320x460 view with a number of buttons, depending on the button pressed, a 280x280 view pops up over the 320x460 view (similar to the behavior of the UIAlertView) using code like this:
UIView *overlayView = [[UIView alloc] initWithFrame:CGRectMake(20, 200, 280, 280)];
overlayView.backgroundColor = [UIColor whiteColor];
[overlayView autorelease];
[overlayView addSubview:label]; // label declared elsewhere
[overlayView addSubview:backgroundImage]; // backgroundImage declared elsewhere
//... Add a bunch of other controls
[label release];
[backgroundImage release];
//... Release a bunch of other controls
[self.view addSubview:overlayView];
Everything works fine displaying the overlayView and all its controls.
The question I have is, how do I get rid of the overlayView once it's displayed? I want to make it not only not visible but to remove it completely, since the user will be popping up the overlayView repeatedly during use.
You need access to overlayView to remove it, I'd suggest adding this to the create side:
overlayView.tag = 5; // Or some other non-zero number
Then later you can use it like this:
-(void)removeOverlayView
{
UIView *overlayView = [self.view viewWithTag:5];
[overlayView removeFromSuperview];
}

iPhone: Add "loading" subView

I am wanting to show a simple loading dialog when certain things are happening in my app. I figured I would just create a new view, add a label to that, and then set that view to a subView of my current view.
When doing this, I don't see anything!
Here is how I am writing my method:
- (void)showLoading {
UIView *loading = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(198, 9, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[super.view addSubview:loading];
[super.view bringSubviewToFront:loading];
[loading release];
[txt release];
}
Am I doing this completely wrong?
EDIT:
I added it to the viewDidLoad method, and it works how I want:
loading = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[txt release];
[self.view addSubview:loading];
[self.view bringSubviewToFront:loading];
But when loading it from a method, it seems to lag, and not show up for a bit.
Although this doesn't directly answer your question, I'd recommend grabbing MBProgressHUD from GitHub and using that in place of a static label. Looks better, less code for you to directly maintain, etc. You can find it at http://github.com/matej/MBProgressHUD
The way I use it is by creating a subclass of UITableViewController and defining a handful of methods to show and hide the HUD view. From there, I call each relevant method when I'm loading or done loading.
Specifically, I have four methods: -hudView, -showLoadingUI, -showLoadingUIWithText:, and -hideLoadingUI.
-hudView creates a new MBProgressHUD object if one doesn't already exist, and adds it to the current view ([self.view addSubview:hudView]).
-showLoadingUI calls -showLoadingUIWithText: with a default title, -showLoadingUIWithText: just unhides the MBProgressHUD and sets a label value for it (self.hudView.labelText = #"foo";).
-hideLoadingUI hides the hudView ([self.hudView hide:YES]).
First, I don't think UIView has method called init. You may just call the super of it. The appropriate method you should call is - (id)initWithFrame:(CGRect)aRect . The frame is the position, the size of the View you want to display. More here
Another thing is why you call [super.view addSubView:], I think it should be self.view, isn't it?