Declare CGRect in if statement - iphone

I have an if statement that checks if an array has a certain item:
if ([[subTitles objectAtIndex:indexPath.row] isEqualToString:#""]) {
and has this code inside it:
CGRect CellFrame = CGRectMake(0, 0, 300, 44);
Then I have an else statement with the same thing but with different values, obviously.
It looks like this all together:
if ([[subTitles objectAtIndex:indexPath.row] isEqualToString:#""]) {
CGRect CellFrame = CGRectMake(0, 0, 300, 44);
} else {
CGRect CellFrame = CGRectMake(0, 0, 300, 60);
}
Later in my code, I am trying to use CellFrame but it says it is undeclared. What is the proper way to make a CGRect if/else statement without having to put all of the code in each statement? Please say if I am unclear so I can provide more detail. Thanks.

You need the variable to be defined outside of the if/else statement. When you put it inside, it is only visible to code in that statement and will be destroyed on exit. Also, since only the height is different, you could assign the other parts of the rectangle outside of the if statement.
CGRect CellFrame;
CellFrame.origin = CGPointMake(0,0);
CellFrame.size.width = 300;
if([[subTitles objectAtIndex:indexPath.row] isEqualToString:#""]) {
CellFrame.size.height = 44;
} else {
CellFrame.size.height = 60;
}

Related

Find real frame of UIVIew

I have a custom UIView I create in my class. I then add it to my screen and I want to know it's CGRect. But from what it seems, I can't seem to get the size of this particular UIView. Here's my code:
CGRect labelFrame;
if (!tickerImage) {
labelFrame = CGRectMake(upperTextField.frame.origin.x + 8, upperTextField.frame.origin.y, 0, 0);
} else {
labelFrame = CGRectMake(upperTextField.frame.origin.x + 16, upperTextField.frame.origin.y, 0, 0);
}
SGNewLabel = [[SGAdressLabel alloc] initWithFrame:labelFrame];
[labelsArray addObject:SGNewLabel];
[self.view addSubview:SGNewLabel];
[SGNewLabel addNewLabelWithText:labelText andTickerImage:tickerImage];
UIView *lastLabel = [labelsArray lastObject];
CGRect newTextFieldFrame = CGRectMake(lastLabel.frame.origin.x + lastLabel.frame.size.width, labelFrame.origin.y, 320, 30);
upperTextField.frame = newTextFieldFrame;
As you see, I set the upperTextField frame based on the frame of my custom SGNewLabel which appears to be wrong, even if put into NSArray. I set 0, 0 at the end of the labelFrame declaration because I don't know the size of the future object and here is the source of my problem.
How can I correct it and get the right size of my UIView?
EDIT:
NSLog on my view gives me this:
NSLog(#"The rect of object is: %#", NSStringFromCGRect(self.frame));
2013-02-11 17:16:02.333 MyApp[2383:c07] The rect of object is: {{24, 55}, {0, 0}}
The UIView itself is drawn normally and I can see it full-sized.
I should have used my label's frame set in its own class and then my code would look like this:
SGNewLabel = [[SGAdressLabel alloc] init];
CGRect labelFrame;
if (!tickerImage) {
labelFrame = CGRectMake(upperTextField.frame.origin.x + 8, upperTextField.frame.origin.y, SGNewLabel.frame.size.width, SGNewLabel.frame.size.height);
} else {
labelFrame = CGRectMake(upperTextField.frame.origin.x + 16, upperTextField.frame.origin.y, SGNewLabel.frame.size.width, SGNewLabel.frame.size.height);
}
SGNewLabel.frame = labelFrame;

status message corrupts main window in my iPhone app

In my iOS app when a button is pressed an event is initiated and at the bottom of the screen appears a status message informing the user about a successful or failed operation.
The status message appear and disappear with the following code.
this one show the message
- (void) showMessage:(NSString*)text
{
CGRect frame = [[self view] frame];
if(statusView == nil)
{
messageViewWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, frame.size.height-29,
frame.size.width, 0)];
statusView = [[StatusMessageView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, 0)
Text:text
andShowIndicator:NO];
[messageViewWindow addSubview:statusView];
[messageViewWindow makeKeyAndVisible];
}
UILabel *label = [statusView getTextLabel];
UIImageView *imageView = [statusView getBackImageView];
CGSize stringSize = [text sizeWithFont:[Constants getLabelFont]];
int xCoordinate = (messageViewWindow.frame.size.width - stringSize.width)/2;
[UIView beginAnimations:nil context:nil];
messageViewWindow.frame = CGRectMake(0, frame.size.height-59, frame.size.width, 30);
statusView.frame = CGRectMake(0, 0, frame.size.width, 30);
label.frame = CGRectMake(xCoordinate,5,stringSize.width,20);
imageView.frame = CGRectMake(0, 0, frame.size.width, 30);
[UIView commitAnimations];
[NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:#selector(hideMessage) userInfo:nil repeats:NO];
}
and this one hides the message
- (void) hideMessage
{
UILabel *label = [statusView getTextLabel];
UIImageView *imageView = [statusView getBackImageView];
CGRect labelFrame = label.frame;
[UIView beginAnimations:nil context:nil];
messageViewWindow.frame = CGRectMake(0, [UIScreen mainScreen].applicationFrame.size.height-29, [UIScreen mainScreen].applicationFrame.size.width, 0);
statusView.frame = CGRectMake(0, 0, [UIScreen mainScreen].applicationFrame.size.width, 0);
label.frame = CGRectMake(labelFrame.origin.x, 0, labelFrame.origin.y, 0);
imageView.frame = CGRectMake(0, 0, [UIScreen mainScreen].applicationFrame.size.width, 0);
[UIView commitAnimations];
}
strange thing is that the message is shown and after two seconds dissapears, however the main window lost functionality, like the copy and paste ability.
Do you see something wrong in my methods? Is there any certain way, calling the "animations" code?
Since you are writing [messageViewWindow makeKeyAndVisible];, you have to write [self.view.window makeKeyAndVisible] in hideMessage to let the self.view.window handle all the userinteraction again.
This is because you use a UIWindow to display your view, which is not a good idea (it is not recommended to create UIWindows by yourself, as explained in the "Overview" paragraph of the documentation.
Moreover, you make your window the keywindow but don't restore the standard window at the end when you hide it, thus your problems with the events later.
And last, you use obsolete APIs for the animations, which is deprecated since iOS4 is out (and I guess you don't code for iOS3 or earlier), you should really use the new APIs that have been introducted 3 iOS versions ago now. Besides, it will avoid the overhead to create an NSTimer for that.
So you code can become:
- (void) showMessage:(NSString*)text
{
CGSize sz = self.view.window.frame.size;
CGRect hiddenFrame = CGRectMake(0, sz.height-29, sz.width, 0);
if(statusView == nil)
{
statusView = [[StatusMessageView alloc] initWithFrame:hiddenFrame
text:text
andShowIndicator:NO];
[self.window addSubview:statusView];
}
UILabel *label = [statusView getTextLabel];
UIImageView *imageView = [statusView getBackImageView];
CGSize stringSize = [text sizeWithFont:[Constants getLabelFont]];
int xCoordinate = (sz.width - stringSize.width)/2;
label.frame = CGRectMake(xCoordinate,5,stringSize.width,20);
imageView.frame = CGRectMake(0, 0, frame.size.width, 30);
[UIView animateWithDuration:1.f animations:^{
statusView.frame = CGRectMake(0, sz.height-59, frame.size.width, 30);
} completion:^{
// When show animation is done, schedule the start of the hide animation with a delay of 2 seconds
[UIView animateWithDuration:1.f delay:2.f options:0 animation:^{
statusView.frame = hiddenFrame;
} completion:nil];
}];
}

How to move CGRect in a custom cell?

So, I have a custom cell class and in the implementation I have this code:
- (void)layoutSubviews {
[super layoutSubviews];
CGRect contentRect = self.contentView.bounds;
CGFloat boundsX = contentRect.origin.x;
CGRect frame;
frame = CGRectMake(boundsX+10,10, 50, 50);
picture.frame = frame;
frame = CGRectMake(boundsX+75 ,0, 170, 50);
nameLabel.frame = frame;
frame = CGRectMake(boundsX+75 ,43, 225, 23);
costLabel.frame = frame;
frame = CGRectMake(boundsX+280, 30, 8, 13);
arrow.frame = frame;
}
Now let's see how this layout works at the picture below:
I am satisfied with the result, but I also want to change costLabel position in the cell according to the nameLabel. If there are two lines of text in the nameLabel, I don't want to change the costLabel position, however if there are just one line of text in the nameLabel, I want to move costLabel upper, so I can rid off the space and make costLabel closer to nameLabel.
I tried this in the cellForRowAtIndexPath:
CGRect frame = cell.costLabel.frame;
frame.origin.y = frame.origin.y-10; // new y coordinate
cell.costLabel.frame = frame;
But it didn't work.
Any solutions or ideas, how can I change CGRect coordinates (y origin) ?
layoutSubviews is going to be called when it wants to draw your cell. Make your frame changes in layoutSubviews (i.e.
if (thereIsOnlyOneLineInThisCell) {
frame = CGRectMake(boundsX+75 ,43 -10, 225, 23);
} else {
frame = CGRectMake(boundsX+75 ,43, 225, 23);
}
costLabel.frame = frame;
You can calclulate the size of the nameLabel with : NSString UIKit additions

Trying to pass CGrect to performSelector withObject

I'm trying to pass a CGRect:
SEL frameSel = NSSelectorFromString(#"setFrame:");
CGRect rect = CGRectMake(10, 10, 200, 100);
[object performSelector:frameSel withObject:rect ];
But this does not compile
I also tried:
SEL frameSel = NSSelectorFromString(#"setFrame:");
CGRect rect = CGRectMake(10, 10, 200, 100);
NSValue * value = [NSValue valueWithCGRect:rect];
[object performSelector:frameSel withObject:value ];
Actually, this does compile but when I debug, the frame is not setted correctly:
po object
<UILabel: 0x39220f0; frame = (0 0; 200 100); text = 'reflectionLabel'; clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x3922240>>
But it should be frame = (10 10; 200 100)
How can I solve this problem?
Thank you in advance!
CGRect rect = CGRectMake(10, 10, 200, 100);
[object performSelector:frameSel withObject:rect ];
But this does not compile
Correct, because rect is a structure, not a pointer to an object.
CGRect rect = CGRectMake(10, 10, 200, 100);
NSValue * value = [NSValue valueWithCGRect:rect];
[object performSelector:frameSel withObject:value ];
Actually, this does compile but when I debug, the frame is not setted correctly:
po object
<UILabel: 0x39220f0; frame = (0 0; 200 100); text = 'reflectionLabel'; clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x3922240>>
Well, it's not garbage, so it would seem that it worked, even though the origin somehow remained zero. You may want to ask a separate question about that.
Anyway, why are you using performSelector:withObject:? You're not addressing it to a specific thread and you're not putting it on a delay; why not simply say object.frame = rect?
You must use an NSInvocation to handle any non-id arguments or return values:
CGRect rect = CGRectMake(104, 300, 105, 106);
UIView *view = [[UIView alloc] init];
NSMethodSignature *methodSig = [view methodSignatureForSelector:#selector(setFrame:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
invocation.target = view;
invocation.selector = #selector(setFrame:);
[invocation setArgument:&rect atIndex:2];
[invocation invoke];

Resizing UITextView

I have a UITextView added on my UIView. The textview added is not editable, it is just to display some data. The data displayed in the textview is dynamic. Thats is the number of lines is not fixed. It may vary. So if the number of line increases, the size of the textview also needs to be increased. I have no clue how to do this. Please give me some ideas.
UPDATE:
Here's what I'm doing:
UIView *baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
baseView.backgroundColor = [UIColor grayColor];
[window addSubview:baseView];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, 30)];
textView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
textView.text = #"asdf askjalskjalksjlakjslkasj";
[textView sizeToFit];
[baseView addSubview:textView];
There is an answer posted at How do I size a UITextView to its content?
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;
or better(taking into account contentInset thanks to kpower's comment)
CGRect frame = _textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = _textView.contentSize.height + inset.top + inset.bottom;
_textView.frame = frame;
note: If you are going to reference a property of an object many times(e.g. frame or contentInset) it's better to assign it to a local variable so you don't trigger extra method calls(_textView.frame/[_textView frame] are method calls). If you are calling this code a lot(100000s of times) then this will be noticeably slower(a dozen or so method calls is insignificant).
However... if you want to do this in one line without extra variables it would be
_textView.frame = CGRectMake(_textView.frame.origin.x, _textView.frame.origin.y, _textView.frame.size.width, _textView.contentSize.height + _textView.contentInset.top + _textView.contentInset.bottom);
at the expense of 5 extra method calls.
You can use setFrame: or sizeToFit.
UPDATE:
I use sizeToFit with UILabel, and it works just fine, but UITextView is a subclass of UIScrollView, so I can understand why sizeToFit doesn't produce the desired result.
You can still calculate the text height and use setFrame, but you might want to take advantage of UITextView's scrollbars if the text is too long.
Here's how you get the text height:
#define MAX_HEIGHT 2000
NSString *foo = #"Lorem ipsum dolor sit amet.";
CGSize size = [foo sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
and then you can use this with your UITextView:
[textView setFont:[UIFont systemFontOfSize:14]];
[textView setFrame:CGRectMake(5, 30, 100, size.height + 10)];
or you can do the height calculation first and avoid the setFrame line:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, size.height + 10)];
sizeToFit Does Work
If you call sizeToFit after you set the text the first time it resizes. So after the first time you set it subsequent calls to set text will result in no change in size. Even if you call sizeToFit.
However, you can force it to resize like this:
Set the text.
Change the textView frame height to be CGFLOAT_MAX.
Call sizeToFit.
textView.contentSize.height in the textViewDidChange can only resize after text actually grows. For best visual result better to resize beforehand. After several hours I've figured out how to make it the same perfectly as in Instagram (it has the best algorithm among all BTW)
Initialize with this:
// Input
_inputBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, size.height - _InputBarHeight, size.width, _InputBarHeight)];
_inputBackgroundView.autoresizingMask = UIViewAutoresizingNone;
_inputBackgroundView.contentMode = UIViewContentModeScaleToFill;
_inputBackgroundView.userInteractionEnabled = YES;
[self addSubview:_inputBackgroundView];
[_inputBackgroundView release];
[_inputBackgroundView setImage:[[UIImage imageNamed:#"Footer_BG.png"] stretchableImageWithLeftCapWidth:80 topCapHeight:25]];
// Text field
_textField = [[UITextView alloc] initWithFrame:CGRectMake(70.0f, 0, 185, 0)];
_textField.backgroundColor = [UIColor clearColor];
_textField.delegate = self;
_textField.contentInset = UIEdgeInsetsMake(-4, -2, -4, 0);
_textField.showsVerticalScrollIndicator = NO;
_textField.showsHorizontalScrollIndicator = NO;
_textField.font = [UIFont systemFontOfSize:15.0f];
[_inputBackgroundView addSubview:_textField];
[_textField release];
[self adjustTextInputHeightForText:#""];
Fill UITextView delegate methods:
- (void) textViewDidBeginEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (void) textViewDidEndEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
if ([text isEqualToString:#"\n"])
{
[self performSelector:#selector(inputComplete:) withObject:nil afterDelay:.1];
return NO;
}
else if (text.length > 0)
{
[self adjustTextInputHeightForText:[NSString stringWithFormat:#"%#%#", _textField.text, text]];
}
return YES;
}
- (void) textViewDidChange:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
And the trick is...
- (void) adjustTextInputHeightForText:(NSString*)text {
int h1 = [text sizeWithFont:_textField.font].height;
int h2 = [text sizeWithFont:_textField.font constrainedToSize:CGSizeMake(_textField.frame.size.width - 16, 170.0f) lineBreakMode:UILineBreakModeWordWrap].height;
[UIView animateWithDuration:.1f animations:^
{
if (h2 == h1)
{
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - _InputBarHeight, self.frame.size.width, _InputBarHeight);
}
else
{
CGSize size = CGSizeMake(_textField.frame.size.width, h2 + 24);
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - size.height, self.frame.size.width, size.height);
}
CGRect r = _textField.frame;
r.origin.y = 12;
r.size.height = _inputBackgroundView.frame.size.height - 18;
_textField.frame = r;
} completion:^(BOOL finished)
{
//
}];
}
This works perfectly for me:
#define MAX_HEIGHT 2000
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
[textview setFont:[UIFont systemFontOfSize:14]];
[textview setFrame:CGRectMake(45, 6, 100, size.height + 10)];
textview.text = text;
Do the following:
_textView.text = someText;
[_textView sizeToFit];
_textView.frame.height = _textView.contentSize.height;
Addressing the similar issue I just created a an auto-layout based light-weight UITextView subclass which automatically grows and shrinks based on the size of user input and can be constrained by maximal and minimal height - all without a single line of code.
https://github.com/MatejBalantic/MBAutoGrowingTextView
The answer given by #Gabe doesn't work in iOS7.1 seemingly until after viewDidAppear. See my tests below.
UPDATE: Actually, the situation is even more complicated. If you assign textView.text in the resizeTheTextView method, in iOS7, the resizing amounts to allowing for only a single line of text. Seriously odd.
UPDATE2: See also UITextView content size different in iOS7
UPDATE3: See my code at the very bottom for what I'm using now. Seems to do the job.
#import "ViewController.h"
#interface ViewController ()
{
UITextView *textView;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textView = [[UITextView alloc] initWithFrame:CGRectMake(50, 50, 200, 1)];
[self.view addSubview:textView];
CALayer *layer = textView.layer;
layer.borderColor = [UIColor blackColor].CGColor;
layer.borderWidth = 1;
textView.text = #"hello world\n\n";
// Calling the method directly, after the view is rendered, i.e., after viewDidAppear, works on both iOS6.1 and iOS7.1
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Change size" forState:UIControlStateNormal];
[button addTarget:self action:#selector(resizeTheTextView) forControlEvents:UIControlEventTouchUpInside];
[button sizeToFit];
CGRect frame = button.frame;
frame.origin.y = 400;
button.frame = frame;
[self.view addSubview:button];
// Works on iOS6.1, but does not work on iOS7.1
//[self resizeTheTextView];
}
- (void) viewWillAppear:(BOOL)animated
{
// Does not work on iOS7.1, but does work on iOS6.1
//[self resizeTheTextView];
}
- (void) viewDidAppear:(BOOL)animated
{
// Does work on iOS6.1 and iOS7.1
//[self resizeTheTextView];
}
- (void) resizeTheTextView
{
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
// 5) From https://stackoverflow.com/questions/728704/resizing-uitextview
CGRect frame = textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = textView.contentSize.height + inset.top + inset.bottom;
textView.frame = frame;
NSLog(#"inset.top: %f, inset.bottom: %f", inset.top, inset.bottom);
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
UPDATE3:
if ([[UIDevice currentDevice] majorVersionNumber] < 7.0) {
CGRect frame = _abstractTextView.frame;
UIEdgeInsets inset = _abstractTextView.contentInset;
frame.size.height = _abstractTextView.contentSize.height + inset.top + inset.bottom;
_abstractTextView.frame = frame;
}
else {
CGSize textViewSize = [_abstractTextView sizeThatFits:CGSizeMake(_abstractTextView.frame.size.width, FLT_MAX)];
_abstractTextView.frameHeight = textViewSize.height;
}
After you add the UITextView to its parent if you set a Content Mode on it then it seems to resize itself automatically.
This means you don't need to work out the height manually and apply a height contraint. It just seems to work!! Tested in iOS7 and iOS8 on iPad.
e.g.
--
textView.contentMode = UIViewContentMode.Center;
--
If anyone can explain why this works it would be much appreciated.. I found it by accident when messing with options in interface builder.
Just set scrollEnabled to NO, or uncheck Scrolling Enabled in the Scroll View section in IB and the UITextView will self-size.