Autoresizing Mask Strange Behavior - ios

I'm in an UIViewController, self.view points to a valid view with a frame of 300x480.
UIView *redView;
UIView *blueView;
//[[self view] setAutoresizingMask:YES]; //WRONG!
[[self view] setAutoresizesSubviews:YES];
[[self view] setClipsToBounds:YES];
redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[redView setBackgroundColor:[UIColor redColor]];
[redView setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
[[self view] addSubview:redView];
blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[blueView setBackgroundColor:[UIColor blueColor]];
[blueView setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
[[self view] addSubview:blueView];
With this snippet of code i was trying
To align the 200x200 red view to the right margin,
To align the 100x100 blue view to the left margin;
but the result as you can see is far from being what i was expecting...
http://imageshack.us/photo/my-images/27/iossimulatorscreenshoto.png/
I've read every single bit of apple documentation and every google result about the autoresizing mask usage, but i still can't figure out why this is happening.
Can someone please explain to me what is happening?
Thanks in advance.

Keep in mind it's an autoREsizing mask, not an autosizing mask. It doesn't do anything until something changes size.
What you want to do is define your views' frames exactly where you want them to be according to the current size of your parent view, i.e. 300 x 480. So for example
redView = [[UIView alloc] initWithFrame:CGRectMake(100, 0, 200, 200)];
blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
You are setting their autoresizing masks correctly as far as the horizontal alignment. If self.view changes size, then redView and blueView will remain locked to the margins.
You should set at least one of flexible top margin, flexible bottom margin, or flexible height. Otherwise the behavior is undefined if the parent view changes height. It's impossible for all three of those parameters to remain fixed within two different heights.
This line of code doesn't make sense:
[[self view] setAutoresizingMask:YES];
It isn't a compiler error because BOOL happens to be castable to UIViewAutoresizing, which is an enum. The effect is that YES = 1 = UIViewAutoresizingFlexibleLeftMargin. Probably not what you wanted. More likely you wanted:
[[self view] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
If you can, you should do all this in Interface Builder. Since it's a WYSIWYG editor, you can SWYG ("see what you get"). You can quickly play around with the options and learn how they work. Much faster than edit - compile - run to see the effects. Even if you can't / won't use Interface Builder for your project, try a sample project with it to learn how autoresizing works. Once you know you can do it in code with less trial and error. But watch out for the gotcha that turning a margin bar on in Interface Builder is equivalent to turning the corresponding FlexibleXYZMargin option off in code.

Related

Objective-C: add subview only work for one view

For each UIImageView, I want to add the label subview to it.
Here is my class inherited form UIImageView
-(instancetype)initWithFrame:(CGRect)frame
{
if (self=[super initWithFrame:frame]) {
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 50)];
self.categoryLabel.textAlignment=NSTextAlignmentCenter;
self.categoryLabel.font=[UIFont systemFontOfSize:20];
self.categoryLabel.textColor=[UIColor whiteColor];
[self addSubview:self.categoryLabel];
NSLog(#"%#",self.subviews);
}
return self;
}
-(void)setModel:(HorizontalModel *)model
{
_model=model;
self.categoryLabel.text=self.model.category;
[self sd_setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"XXXXX%#",self.model.imgURL]] placeholderImage:[UIImage imageNamed:#"obama"]];
}
Here is my code in the view controller.
-(void)addImage:(NSNotification *)notification
{
self.HArrayLists=notification.userInfo[#"array"];
for (int i=0; i<[self.HArrayLists count]; i++) {
JTImageView *imageView=[[JTImageView alloc] initWithFrame:CGRectMake(i*310, 0, 300, 200)];
imageView.model=[HorizontalModel restaurantsDetailWithDict: self.HArrayLists[i]];
[self.mediaScrollView addSubview:imageView];
}
self.mediaScrollView.contentSize=CGSizeMake(310*[self.HArrayLists count], 0);
}
It turns out that only the first imageView shows a label, while the rest of the imageViews show only images.
I think the core of your problem is the line:
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 50)];
You are offsetting the x and y positions of the label by the x and y values of the image. This will place them outside the area of the image and with the image clipping, make them invisible. I think the line should be
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, 50)];
to place all the labels at the top left corner of each image.
Having said that there are also a number of recommendations I would like to offer.
Firstly make all variable names start with a lowercase. So self.HArrayLists should be self.hArrayLists.
Secondly try and make variable names match their contents. So again looking at self.HArrayLists, perhaps something like self.imageData.
Next I would have done the composition differently. I would have a UIView to which I add both the UILabel and UIImageView instances. Using a parent view like this to layout two sub views often makes life easier.
I would also look into using a UICollectionView and UICollectionViewController rather than a UIScrollView. It will take you a bit of work to get your heads around how collection views work. But you will gain in terms of performance and better layout management.
Finally, study up on constraints. They're an essential part of building modern apps that can easily adapt to different sized screens, rotation and layouts.
You need to set as categoryLabel's frame properly.
self.categoryLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, frame.origin.y, frame.size.width, 50)];

Can't centre UITextField in UIView

I'm trying to add my UITextField to a UIView that is placed in the centre of the table. The UIView works fine and is positioned correctly. however the UITextField is in the wrong position and at the bottom left of the screen. Code below:
self.addFriendView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
self.addFriendView.center=self.view.center;
[self.addFriendView setBackgroundColor:[UIColor whiteColor]];
UITextField *nameField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 280, 40)];
nameField.delegate=self;
nameField.center=self.view.center;
nameField.placeholder=#"enter username";
[self.addFriendView addSubview:nameField];
[self.tableView.superview addSubview:self.addFriendView];
When I am placing the UITextField in the centre of the UIView do the coordinates need to be inside the UIView's coordinates or the frames?
That's because addFriendView.center is it's center in it's superview coordinate which is {150, 25}. What you want is that put the center of nameField in the center of addFriendView in addFriendView's coordinate.
So, use this:
nameField.center = CGPointMake(CGRectGetMidX(addFriendView.bounds),
CGRectGetMidY(addFriendView.bounds));
Updated to use CGRectGetMidX and CGRectGetMidY instead.
Because you have added nameField as a subview of self.addFriendView, its center will need to be relative to self.addFriendView.
nameField.center=self.view.center;
This line here is causing your issue. If fact, it's also an issue where you assign self.addFriendView's center, but by luck, self.view's bounds happen to be the same as its superview's.
Instead, you'll want to do this:
nameField.center=CGPointMake(CGRectGetMidX(self.addFriendView.bounds),
CGRectGetMidY(self.addFriendView.bounds));
and also, just for robustness:
self.addFriendView.center=CGPointMake(CGRectGetMidX(self.view.bounds),
CGRectGetMidY(self.view.bounds));

Programmatically create subview that covers the top 50% of the iPhone screen

I have a subview and I have been customizing its size by using the frame property and setting its value to the CGRectMake function's parameter values.
I have slowly but surely been changing the CGRectMake parameters and re-running the app to get the subview to the correct position on the screen but I know there has to be an easier way.
Here is what I am currently doing:
UIImageView *halfView = [[UIImageView alloc]initWithImage:image];
[self.view addSubview:halfView];
halfView.frame = CGRectMake(0, 0, 320, 270);
Is there a way that I can stop having to manually enter those 4 parameters into CGRectMake, and just set it to the top 50% of the screen?
Here is what I want the subview to look like on the iphone's screen:
halfView.frame = CGRectMake(0, 0, 320, self.view.bounds.size.height/2);
you just take the height and divide it by two
You should also probably change 320 to self.view.bounds.size.width
I suggest reading this post to really get a grasp of UIViews and working with them:
UIView frame, bounds and center

Constrain UIScrollView panning?

UIScrollView has a built-in behavior "directionLockEnabled".
When enabled, panning will attempt to lock to either the horizontal or vertical directions. But when the user aggressively attempts to scroll diagonally - it still allows diagonal scrolling.
I'd like to remove the ability to diagonally scroll.
Many thanks in advance.
EDIT:
I've tried putting a UIScrollView inside another (each with dimensions so as to constrain the movement) but the pan recognizers seemed to conflict - only one worked.
I've looked at TTScrollView to see if it could be modified for the job - but it does not seem to be working properly out of the box (freezing after first gesture.)
I've tried adding a second action (listener) to UIScrollView.panGestureRecognizer to call setTranslation:inView with a constrained value. This resulted in erratic jumping.
And several other avenues probably not worth mentioning.
EDIT2:
Odrakir's solution works. Code looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIImage* image = [UIImage imageNamed:#"Sofia"];
UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 0, 1024 * 2, 768 * 2);
UIScrollView* scrollView1 = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024*2, 768)];
scrollView1.contentSize = CGSizeMake(1024*2, 768*2);
[scrollView1 addSubview:imageView];
UIScrollView* scrollView2 = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
scrollView2.contentSize = CGSizeMake(1024*2, 768);
[scrollView2 addSubview:scrollView1];
[self.view addSubview:scrollView2];
}
I never tried that, but I have an app that has a horizontal ScrollView, and inside it there are multiple views, each of them with their own vertical ScrollView.
It's kind of like a magazine, with different articles and each article has a number of pages.
Both ScrollViews work and the user can't scroll diagonally. You can try that.
The display hierarchy is Horizontal ScrollView-> View ->Vertical ScrollView

Applying transform to UITextView - prevent content resizing

When I apply a rotation transform to a UITextView and then click inside to begin editing, it appears that the content size is automatically being made wider. The new width of the content view is the width of the rotated view's bounding box. For example, given a text box of width 500 and height 400, and rotated by 30 degrees, the new content width would be:
(500 * cos(30)) + (400 * sin(30)) = 633
Or graphically:
Interestingly, if you are already editing the text view and THEN apply the transform, then it appears that no modification is made to the content size. So it appears that sometime around the start of text editing, the text view looks at its frame property and adjusts the content size based on the frame width. I imagine the solution to this is to tell it to use the bounds property instead, however I don't know where to do this, as I'm not sure exactly where the text view is deciding to modify the content size.
I have googled but can't seem to find any references to using transformed UITextViews. Does anybody have any ideas about this?
EDIT (button action from test project):
- (IBAction)rotateButtonTapped:(id)sender {
if (CGAffineTransformIsIdentity(self.textView.transform)) {
self.textView.transform = CGAffineTransformMakeRotation(30.0 * M_PI / 180.0);
}
else {
self.textView.transform = CGAffineTransformIdentity;
}
NSLog(#"contentsize: %.0f, %.0f", textView.contentSize.width, textView.contentSize.height);
}
I was also stuck with this problem.
The only solution which I found was to create an instance of UIView and add the UITextView as a subview. Then you can rotate the instance of UIView and UITextView will work just fine.
UITextView *myTextView = [[UITextView alloc] init];
[myTextView setFrame:CGRectMake(0, 0, 100, 100)];
UIView *myRotateView = [[UIView alloc] init];
[myRotateView setFrame:CGRectMake(20, 20, 100, 100)];
[myRotateView setBackgroundColor:[UIColor clearColor]];
[myRotateView addSubview:myTextView];
myRotateView.transform = CGAffineTransformMakeRotation(0.8);
[[self view] addSubview:myRotateView];
Have you tried applying the rotation by doing a layer transform rather than a transform on the view?
#import <QuartzCore/QuartzCore.h>
mytextField.layer.transform = CATransform3DMakeRotation (angle, 0, 0, 1);
This might be enough to trick whatever broken logic exists inside the core text field code.

Resources