4

I do this quite a bit in my code:

self.sliderOne.frame  = CGRectMake(newX, 0, self.sliderOne.frame.size.width, self.sliderOne.frame.size.height); 

Is there any way to avoid this tedious code? I have tried this type of thing:

self.sliderOne.frame.origin.x = newX;

but I get a Lvalue required as left operand of assignment error.

dandan78
  • 13,328
  • 13
  • 64
  • 78
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421

3 Answers3

8

I finally followed @Dave DeLong's suggestion and made a category. All you have to do is import it in any class that wants to take advantage of it.

UIView+AlterFrame.h

#import <UIKit/UIKit.h>

@interface UIView (AlterFrame)

- (void) setFrameWidth:(CGFloat)newWidth;
- (void) setFrameHeight:(CGFloat)newHeight;
- (void) setFrameOriginX:(CGFloat)newX;
- (void) setFrameOriginY:(CGFloat)newY;

@end

UIView+AlterFrame.m

#import "UIView+AlterFrame.h"

@implementation UIView (AlterFrame)

    - (void) setFrameWidth:(CGFloat)newWidth {
        CGRect f = self.frame;
        f.size.width = newWidth;
        self.frame = f;
    }

    - (void) setFrameHeight:(CGFloat)newHeight {
        CGRect f = self.frame;
        f.size.height = newHeight;
        self.frame = f;
    }

    - (void) setFrameOriginX:(CGFloat)newX {
        CGRect f = self.frame;
        f.origin.x = newX;
        self.frame = f;
    }

    - (void) setFrameOriginY:(CGFloat)newY {
        CGRect f = self.frame;
        f.origin.y = newY;
        self.frame = f;
    }

@end

I could DRY up the methods using blocks... I'll do that at some point soon, I hope.

Later: I just noticed CGRectOffset and CGRectInset, so this category could be cleaned up a bit (if not eliminated altogether).

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
  • 1
    always good on category methods of frameworks you don't own to mark the methods with a unique prefix. In Google Toolbox for Mac we use gtm_ so this would be gtm_setFrameWidth: etc. That way if Apple decides in the future to define setFrameWidth: it doesn't conflict with your category. – dmaclach Jul 16 '10 at 23:02
  • Thanks @dmaclach, this all seems so primitive compared to Java, which of course doesn't let you modify framework classes at all :) I will take your advice. – Dan Rosenstark Jul 16 '10 at 23:47
  • @dmaclach, I have to say that while I did mark a best answer to this question, I still don't understand why you can't just do `self.frame.size.width = 30;'... – Dan Rosenstark Jul 17 '10 at 20:12
  • In short, dot notation is used for property access _and_ struct members. Here be dragons. http://weblog.bignerdranch.com/?p=83 ... http://stackoverflow.com/questions/5860755/how-to-gain-assignment-access-to-cgrect-elements-when-the-cgrect-is-an-instance-v/5861985#5861985 – Joe D'Andrea Jul 28 '11 at 16:18
  • Thanks @Joe D'Andrea, obviously I've figured out about lvars and rvars in the intervening year, but it's good to have the links. Do you use property notation? I was thinking about giving it up, but I think it's the way the world is going. – Dan Rosenstark Jul 28 '11 at 19:47
  • Having said "here be dragons", of course, here's the punchline: Yes, I DO use dot notation, very carefully. Look back at the bignerdranch.com post from my previous comment, and check out Chris Hanson's weigh-in. (He should know. He's at Apple and very much involved with all things Xcode/Objective-C!) More: http://eschatologist.net/blog/?p=160 – Joe D'Andrea Jul 29 '11 at 01:54
  • @Joe D'Andrea what I don't understand is why I can call [obj doThis] even if obj is nil yet obj.doThis will blow up if it's nil. I'm now pretty fast in obj-c, but I find a lot of the language and runtime features to not be by-design but rather accidental. But yeah, point taken about using properties for state, methods for behavior. – Dan Rosenstark Jul 29 '11 at 04:24
  • 1
    For obj = nil, [obj doThis] ends up messaging nil, which is fine. For the dot notation case, if I'm reading this correctly, you're either effectively assigning to an lvalue of nil (bzzzt!) or trying to call 'nil' without using Objective-C's bracket notation (also bzzzt!). The "do" in "doThis" tells me that "doThis" is more of a behavior, not an indication of state. Thus, you should use bracket notation in that case. Use dot notation only to get and set object state (which, it turns out, can also have some nice side effects if you want). See Chris Hanson's article - he explains it really well! – Joe D'Andrea Aug 01 '11 at 14:48
  • NOTE: now that AppCode is amazing, refactoring to use correct prefixes as suggested by @dmaclach is MUCH easier, even way after you got the original advice ;) – Dan Rosenstark Oct 19 '13 at 20:59
4

Yeah, you have to do:

CGRect newFrame = self.sliderOne.frame;
newFrame.origin.x = frame.size.width - MARGIN * 2 - totalWidth;
self.sliderOne.frame = newFrame;

It sucks, I know. If you find yourself doing this a lot, you may want to add categories to UIView/NSView to alter this stuff for you:

@interface UIView (FrameMucking)

- (void) setWidth:(CGFloat)newWidth;

@end

@implementation UIView (FrameMucking)
 - (void) setWidth:(CGFloat)newWidth {
  CGRect f = [self frame];
  f.size.width = newWidth;
  [self setFrame:f];
}
@end

Etc.

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • Wow. I didn't know categories are the objective-c (or C?) mechanism for monkey patching. Very interesting. – Dan Rosenstark Jul 06 '10 at 22:25
  • @yar they are a mechanism, and they're one of my most favorite Objective-C language features. :) – Dave DeLong Jul 06 '10 at 22:36
  • wow I just noticed that you're saying that you can set the width using `f.size.width=`... I hope that's true. – Dan Rosenstark Jul 07 '10 at 04:44
  • @Daniel `f` is a C structure, which means `f.size.width = 42` will change a variable that's being stored locally. You then have to apply those changes into the UIView via `setFrame:`, which wants a structure as the parameter. I've done this dozens of times. – Dave DeLong Jul 07 '10 at 05:55
  • @Dave DeLong: what I don't get is why I am allowed to change f.size.width and not able to change self.frame.size.width... that's the heart of the question. – Dan Rosenstark Jul 08 '10 at 12:35
  • @Dave DeLong, I took your lead and wrote up the AlterFrame category below. Very cool. – Dan Rosenstark Jul 16 '10 at 22:24
  • Just noticed CGRectOffset and CGRectInset. Pretty neat. – Dan Rosenstark Sep 04 '11 at 21:49
2

The issue here is that self.sliderOne.frame.origin.x is the same thing as [[self sliderOne] frame].origin.x. As you can see, assigning back to the lValue here is not what you want to do.

So no, that "tedious" code is necessary, although can be shortened up a bit.

CGRect rect = thing.frame;
thing.frame = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(rect) + 10, etc...);
Joshua Weinberg
  • 28,598
  • 2
  • 97
  • 90