可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question was originally asked for the objective-c programming language. At the time of writing, swift didn't even exist yet.
Question
Is it possible to change only one property of a CGRect
?
For example:
self.frame.size.width = 50;
instead of
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
50);
of course I understand that self.frame.size.width
is read only so I'm wondering how to do this?
CSS ANALOGY proceed at your own risk
for those of you who are familiar with CSS
, the idea is very similar to using:
margin-left: 2px;
instead of having to change the whole value:
margin: 5px 5px 5px 2px;
回答1:
To answer your original question: yes, it's possible to change just one member of a CGRect
structure. This code throws no errors:
myRect.size.width = 50;
What is not possible, however, is to change a single member of a CGRect
that is itself a property of another object. In that very common case, you would have to use a temporary local variable:
CGRect frameRect = self.frame;
frameRect.size.width = 50;
self.frame = frameRect;
The reason for this is that using the property accessor self.frame = ...
is equivalent to [self setFrame:...]
and this accessor always expects an entire CGRect
. Mixing C-style struct
access with Objective-C property dot notation does not work well in this case.
回答2:
I liked Ahmed Khalaf's answer, but it occurred to me that you may as well just write out a few C functions... the key advantage being that it'll be easier to track down errors in the event that you're using the wrong type.
Having said that, I wrote a .h file with these function declarations:
CGRect CGRectSetWidth(CGRect rect, CGFloat width);
CGRect CGRectSetHeight(CGRect rect, CGFloat height);
CGRect CGRectSetSize(CGRect rect, CGSize size);
CGRect CGRectSetX(CGRect rect, CGFloat x);
CGRect CGRectSetY(CGRect rect, CGFloat y);
CGRect CGRectSetOrigin(CGRect rect, CGPoint origin);
And a corresponding .m file with these function implementations:
CGRect CGRectSetWidth(CGRect rect, CGFloat width) {
return CGRectMake(rect.origin.x, rect.origin.y, width, rect.size.height);
}
CGRect CGRectSetHeight(CGRect rect, CGFloat height) {
return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, height);
}
CGRect CGRectSetSize(CGRect rect, CGSize size) {
return CGRectMake(rect.origin.x, rect.origin.y, size.width, size.height);
}
CGRect CGRectSetX(CGRect rect, CGFloat x) {
return CGRectMake(x, rect.origin.y, rect.size.width, rect.size.height);
}
CGRect CGRectSetY(CGRect rect, CGFloat y) {
return CGRectMake(rect.origin.x, y, rect.size.width, rect.size.height);
}
CGRect CGRectSetOrigin(CGRect rect, CGPoint origin) {
return CGRectMake(origin.x, origin.y, rect.size.width, rect.size.height);
}
So, now, to do what you want, you can just do:
self.frame = CGRectSetWidth(self.frame, 50);
Get even Fancier (update I made a year later)
This has a redundant self.frame
in it, though. To fix that, you could add a category
on UIView
with methods that look like this:
- (void) setFrameWidth:(CGFloat)width {
self.frame = CGRectSetWidth(self.frame, width); // You could also use a full CGRectMake() function here, if you'd rather.
}
And now you can just type in:
[self setFrameWidth:50];
Or, even better:
self.frameWidth = 50;
And just so you can do something like this:
self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
You'll need to also have this in your category:
- (CGFloat) frameWidth {
return self.frame.size.width;
}
Enjoy.
回答3:
Based on ArtOfWarfare's solution (which is really awesome) I've build the UIView
category without C-functions.
Usage examples:
[self setFrameWidth:50];
self.frameWidth = 50;
self.frameWidth += 50;
self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
Header file UIView+easy_frame.h
:
@interface UIView (easy_frame)
- (void) setFrameWidth:(CGFloat)width;
- (void) setFrameHeight:(CGFloat)height;
- (void) setFrameX:(CGFloat)x;
- (void) setFrameY:(CGFloat)y;
- (CGFloat) frameWidth;
- (CGFloat) frameHeight;
- (CGFloat) frameX;
- (CGFloat) frameY;
Implementation file UIView+easy_frame.m
:
#import "UIView+easy_frame.h"
@implementation UIView (easy_frame)
# pragma mark - Setters
- (void) setFrameWidth:(CGFloat)width
{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
width,
self.frame.size.height);
}
- (void) setFrameHeight:(CGFloat)height
{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
height);
}
- (void) setFrameX:(CGFloat)x
{
self.frame = CGRectMake(x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height);
}
- (void) setFrameY:(CGFloat)y
{
self.frame = CGRectMake(self.frame.origin.x,
y,
self.frame.size.width,
self.frame.size.height);
}
# pragma mark - Getters
- (CGFloat) frameWidth
{
return self.frame.size.width;
}
- (CGFloat) frameHeight
{
return self.frame.size.height;
}
- (CGFloat) frameX
{
return self.frame.origin.x;
}
- (CGFloat) frameY
{
return self.frame.origin.y;
}
回答4:
If you find yourself needing to do this sort of individual component modification, it may be worthwhile having macros like these somewhere accessible by all of your code:
#define CGRectSetWidth(rect, w) CGRectMake(rect.origin.x, rect.origin.y, w, rect.size.height)
#define ViewSetWidth(view, w) view.frame = CGRectSetWidth(view.frame, w)
This way, whenever you need to change the width alone - you would simply write
ViewSetWidth(self, 50);
instead of
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, 50);
回答5:
In Swift you could extend the UIView class for the app,
such that you could, for example, move the table header view up by 10 points like this:
tableView.tableHeaderView!.frameAdj(0, -20, 0, 0)
(of course if you're using AutoLayout, that might not actually do anything... :) )
By extending UIView (affecting every UIView and subclass of it in your app)
extension UIView {
func frameAdj(x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) {
self.frame = CGRectMake(
self.frame.origin.x + x,
self.frame.origin.y + y,
self.frame.size.width + width,
self.frame.size.height + height)
}
}
回答6:
Based on n0_quarter answer, here's a UIView Extension written in Swift:
/**
* Convenience category for manipulating UIView frames
*/
extension UIView {
//MARK: - Getters
func frameX() -> CGFloat {
return frame.origin.x
}
func frameY() -> CGFloat {
return frame.origin.y
}
func frameWidth() -> CGFloat {
return frame.size.width
}
func frameHeight() -> CGFloat {
return frame.size.height
}
//MARK: - Setters
func setFrameX(x: CGFloat) {
frame = CGRect(x: x, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
}
func setFrameY(y: CGFloat) {
frame = CGRect(x: frame.origin.x, y: y, width: frame.size.width, height: frame.size.height)
}
func setFrameWidth(width: CGFloat) {
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: frame.size.height)
}
func setFrameHeight(height: CGFloat) {
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
}
}
回答7:
Hope I am not too late to the party, here's my solution in ObjC.
For me I prefer to stick to a single function instead of several functions, and I like the CSS approach shown by the poster. Below is the function I add to my UIView Category.
- (void)resetFrame:(CGRect)frame {
CGRect selfFrame = self.frame;
selfFrame.origin = CGPointMake(frame.origin.x>0 ? frame.origin.x : selfFrame.origin.x, frame.origin.y>0 ? frame.origin.y : selfFrame.origin.y);
selfFrame.size = CGSizeMake(frame.size.width>0 ? frame.size.width : selfFrame.size.width, frame.size.height>0 ? frame.size.height : selfFrame.size.height);
[self setFrame:selfFrame]; }
Shown in the example, it looks at each values in the provided CGRect, and if the value is more than 0, it means we want to replace that value. And therefore, you can do CGRect(0,0,100,0) and it will assume we want to replace the width only.
回答8:
frequently call setFrame method is not so good, like:
setFrameX(x)
setFrameY(y)
setFrameWidth(width)
...
instead, in swift we can make use of default parameters to set multiple parameters only in one call:
func setFrame(x x: CGFloat = CGFloat.NaN, y: CGFloat = CGFloat.NaN, width: CGFloat = CGFloat.NaN, height: CGFloat = CGFloat.NaN) {
let newX = x.isNaN ? self.frame.origin.x : x
let newY = y.isNaN ? self.frame.origin.y : y
let newWidth = width.isNaN ? self.bounds.size.width : width
let newHeight = height.isNaN ? self.bounds.size.height : height
self.frame = CGRect(x: newX, y: newY, width: newWidth, height: newHeight)
}
then update parameters of frame only need:
setFrame(x: x, width: min(maxX - x, titleLabel.bounds.size.width))
setFrame(width: titleDescriptionLabel.bounds.size.width + 5, height: titleDescriptionLabel.bounds.size.height + 6)
回答9:
-(void) sizeScreen : (UIView *) size : (double) width : (double) height : (double) x : (double) y {
CGRect frameRect = size.frame;
frameRect.size.width = width;
frameRect.size.height = height;
frameRect.origin.x = x;
frameRect.origin.y = y;
self.view.frame = frameRect;
}
its work great for me :)