【问题标题】:iOS frame change one property (eg width)iOS框架改变一个属性(例如宽度)
【发布时间】:2012-03-21 05:00:30
【问题描述】:

这个问题最初是针对 Objective-c 编程语言提出的。在撰写本文时,Swift 甚至还不存在。

问题

是否可以仅更改 CGRect一个 属性?

例如:

self.frame.size.width = 50;

而不是

self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        50);

当然我知道self.frame.size.width只读的,所以我想知道如何做到这一点?

CSS ANALOGY 后果自负

对于那些熟悉CSS的人来说,这个想法非常类似于使用:

margin-left: 2px;

而不必更改整个值:

margin: 5px 5px 5px 2px;

【问题讨论】:

  • self 是 UIView 吗?如果是这样,则 frame 属性不是只读的。 The docs 说如果你设置了它会重绘。您应该可以只更新其中的一个字段。
  • 还要检查.offsetBy函数stackoverflow.com/a/43380510/5790492

标签: ios objective-c cgrect cgrectmake


【解决方案1】:
-(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;

}

它对我很有用:)

【讨论】:

    【解决方案2】:

    如果您发现自己需要对单个组件进行此类修改,则可能值得在您的所有代码都可以访问的地方使用这些宏:

    #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)
    

    这样,每当您需要单独更改宽度时 - 您只需编写

    ViewSetWidth(self, 50);
    

    而不是

    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, 50);
    

    【讨论】:

    • 非常好的建议,虽然我只在 CGRectSetWidth 中添加了(以及高度、x 和 y 的设置器)。在我看来,将 CPP 语法与 Obj-C 语法混合看起来会令人困惑......
    【解决方案3】:

    经常调用setFrame方法不是很好,比如: setFrameX(x) setFrameY(y) setFrameWidth(width) ...

    相反,我们可以使用默认参数在一次调用中设置多个参数:

    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)
    }
    

    那么只需要更新frame的参数:

    setFrame(x: x, width: min(maxX - x, titleLabel.bounds.size.width))
    setFrame(width: titleDescriptionLabel.bounds.size.width + 5, height: titleDescriptionLabel.bounds.size.height + 6)
    

    【讨论】:

      【解决方案4】:

      希望我参加聚会还不算太晚,这是我在 ObjC 中的解决方案。

      对我来说,我更喜欢坚持使用单个函数而不是多个函数,并且我喜欢海报中展示的 CSS 方法。下面是我添加到 UIView 类别的函数。

      - (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]; }
      

      如示例所示,它查看提供的 CGRect 中的每个值,如果该值大于 0,则表示我们要替换该值。因此,您可以执行 CGRect(0,0,100,0) ,它会假设我们只想替换宽度。

      【讨论】:

        【解决方案5】:

        基于ArtOfWarfare's solution(非常棒),我构建了UIView 类别,但没有使用 C 函数。

        用法示例:

        [self setFrameWidth:50];
        self.frameWidth = 50;
        self.frameWidth += 50;
        self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
        

        头文件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;
        

        实现文件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;
        }
        

        【讨论】:

          【解决方案6】:

          在 Swift 中,您可以为应用扩展 UIView 类, 例如,您可以将表格标题视图向上移动 10 个点,如下所示:

              tableView.tableHeaderView!.frameAdj(0, -20, 0, 0)
          

          (当然,如果您使用的是 AutoLayout,那实际上可能不会做任何事情...... :))

          通过扩展 UIView(影响应用中的每个 UIView 及其子类)

          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)
              }
          }
          

          【讨论】:

            【解决方案7】:

            根据 n0_quarter 的回答,这是一个用 Swift 编写的 UIView 扩展:

            /**
            *  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)
                }
            }
            

            【讨论】:

            • 所以这是基于 n0_quarter 的,它是基于我的,它是基于 arkuana 的。答案现在已经从 CPP 到 C 再到 Obj-C 再到 Swift。
            【解决方案8】:

            我喜欢Ahmed Khalaf's answer,但我突然想到,您不妨只写一些 C 函数……关键优势是,如果您遇到错误,则更容易追踪错误使用了错误的类型。

            话虽如此,我用这些函数声明编写了一个 .h 文件:

            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);
            

            还有一个对应的.m文件,里面有这些函数实现:

            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);
            }
            

            所以,现在,要做你想做的事,你可以这样做:

            self.frame = CGRectSetWidth(self.frame, 50);
            

            Fancier(我一年后更新)

            不过,这里面有一个多余的self.frame。要解决这个问题,您可以在 UIView 上添加一个 category,其方法如下所示:

            - (void) setFrameWidth:(CGFloat)width {
                self.frame = CGRectSetWidth(self.frame, width); // You could also use a full CGRectMake() function here, if you'd rather.
            }
            

            现在您只需输入:

            [self setFrameWidth:50];
            

            或者,甚至更好:

            self.frameWidth = 50;
            

            这样你就可以做这样的事情:

            self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
            

            您还需要在您的类别中包含此内容:

            - (CGFloat) frameWidth {
                return self.frame.size.width;
            }
            

            享受吧。

            【讨论】:

            • 作为对艾哈迈德使用#define 方法的额外奖励......一两周前有人向我指出#define 相当邪恶。它不是上下文敏感的,所以如果你有类似#define my 8 的东西,它会用8Rect 替换类似myRect 的东西......从今以后,我决定不再使用#define。跨度>
            • #define 有时间和地点,但由于您提到的原因,选择在您的代码中保证唯一的定义名称是有意义的,例如#define MY_SUBCLASS_LEFT_MARGIN 10
            • @CarlosP No. const int MY_SUBCLASS_LEFT_MARGIN = 10; - 您唯一应该使用 #define 的时间是只能在编译时知道的内容,例如有关编译器的详细信息或正在编译的版本可能是定义。假设您正在处理一个大型项目,而其他地方的某个人拥有#define MARGIN 8。现在您的定义被破坏并且构建失败。添加const 是有原因的,出于某种原因,我需要抨击这么多 C、C++ 和 Obj-C 程序员的头脑,因为他们决心滥用 CPP 的#define
            • 那永远不会发生。正如我在上面明确提到的,我确保我创建的任何#defines 都有一个特定于并且只针对它们定义的类的唯一名称。所以我当然不会输入#define MARGIN.. 也就是说,我可能为了方便/速度而过度使用#define,并在船上发表您的评论。
            • @CarlosP - 很高兴你有自己的约定。你曾经和另一个人一起工作过吗?另外两个人?还有十个人?更多的?最终,有人会进来打破你仔细考虑过的#define 命名约定。 const 于 24 年前添加到 C89 - 在我出生之前 - 请使用它。不使用它就像使用一个 while() 循环,它之前有一个初始化,最后有一个递增......是的,这是有效的,但是 for 循环的存在是有原因的,忽略它会导致很容易避免,但很难追查,稍后会出现错误。
            【解决方案9】:

            回答您最初的问题:是的,可以只更改CGRect 结构的一个成员。此代码不会引发任何错误:

            myRect.size.width = 50;
            

            然而,不可能是更改CGRect 的单个成员,该成员本身就是另一个对象的属性。在这种非常常见的情况下,您将不得不使用临时局部变量:

            CGRect frameRect = self.frame;
            frameRect.size.width = 50;
            self.frame = frameRect;
            

            这样做的原因是使用属性访问器self.frame = ... 等效于[self setFrame:...],并且此访问器总是需要一个完整的CGRect。在这种情况下,将 C 风格的 struct 访问与 Objective-C 属性点表示法混合使用效果不佳。

            【讨论】:

            • @OleBegermann 最好的部分是你(43k)说是,而米洛斯(26)说不。我只是不知道该怎么办!
            • 正如我所说,它通常可以工作,但如果CGRect 是Objective-C 对象的属性,它会,如您的示例所示。 Milos 可能指的是您的示例代码,并且在这方面是正确的。
            • 只是提到这一点 - self.frame.size.width = 50 在 Swift 中工作。
            • 此外,我相信如果您在使用 Obj-C 时遇到问题,我近两年的回答会更有用:stackoverflow.com/a/14116702/901641
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-14
            • 1970-01-01
            • 2016-12-12
            • 1970-01-01
            • 2012-08-25
            相关资源
            最近更新 更多