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];
}
Related
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.
I have a question regarding the implementation of loadView:
Right now, I have it like this:
- (void)loadView
{
image = [UIImage imageNamed:#"plan.gif"];
scrollView=[[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
imageView = [[UIImageView alloc] initWithFrame:
CGRectMake(0, 0,scrollView.frame.size.width + 40, scrollView.frame.size.height)];
imageView.image = image;
[imageView setContentMode:UIViewContentModeCenter];
scrollView.contentSize = image.size;
scrollView.maximumZoomScale = 3.0;
scrollView.bounces = NO;
scrollView.delegate = self;
// do any further configuration to the scroll view
// add a view, or views, as a subview of the scroll view.
[scrollView addSubview:imageView];
// release scrollView as self.view retains it
self.view=scrollView;
[scrollView release];
}
I suppose some of it should be in viewDidLoad:?
Thanks in advance.
This seems fine to me.
viewDidLoad is normally used as your hook after getting a view returned from IB. In this case you are essentially just doing the work of IB in code (setting up the view heirachy and configuring it).
Therefore in this case I think splitting out the logic may be superfluous unless it makes your code more readable.
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];
}
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?
I'm doing an iPhone ap which contains a viewController class (PhotoListViewController). This viewController's view has a subview which contains a button attached as a subview. I'm trying to use the addTarget:action:forControlEvent method to call an instance method of the PhotoListViewController class in order to perform an action when the button is pressed but the method is not being called. Any thoughts on how to make this work?
Here's the pertinent code. I know your first though will be why am I nesting subviews, but this just a distilled version of my program - there are reasons I'm doing the nesting. I know there are probably other ways of setting up the program so I don't have to do this nesting, but my real question is - why does this not work?
/Code/
#implementation PhotoListViewController
- (void)loadView {
[super viewDidLoad];
//load elements into the viewController
//first create the frame
CGRect frameParent = CGRectMake(0, 0, 0, 0);
UIView *viewPerson = [[UIView alloc] initWithFrame:frameParent];
CGRect frameChild = CGRectMake(0, 20, 0, 0);
UIView *newView = [[UIView alloc] initWithFrame:frameChild];
UIButton *btnShow = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btnShow.frame = CGRectMake(230, 120, 80, 30);
[btnShow setTitle:#"View!" forState:UIControlStateNormal ];
[btnShow addTarget:self
action:#selector(viewPhoto_click:)
forControlEvents:UIControlEventTouchUpInside];
[newView addSubview:btnShow];
[viewPerson addSubview:newView];
[newView release];
[self setView:viewPerson];
[viewPerson release];
}
- (void)viewPhoto_click:(UIButton*)sender {
NSLog(#"HI");
}
It may not be related, but both viewPerson and newView have a width and height of 0. You may want to give these views a non-null size (the 3rd and 4th arguments of CGRectMake).
action:#selector(viewPhoto_click:)
forControlEvents:UIControlEventTouchUpInside];
May be a copy paste typo but have you tried to remove the : after the viewPhoto_click after the selector?
Also. In most examples the event Handler look like this.
- (void) viewPhoto_click: (id)sender
Not sure if it makes any difference though