2012-03-02 14 views
34

Câu hỏi này ban đầu được yêu cầu cho ngôn ngữ lập trình mục tiêu-c. Tại thời điểm viết, nhanh chóng thậm chí còn chưa tồn tại.Khung iOS thay đổi một thuộc tính (ví dụ: chiều rộng)

Câu hỏi

Có thể thay đổi chỉ một tài sản của một CGRect?

Ví dụ:

self.frame.size.width = 50; 

thay vì

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

tất nhiên tôi hiểu rằng self.frame.size.width được chỉ đọc vì vậy tôi tự hỏi làm thế nào để làm điều này?

CSS tương tựtiến hành có nguy cơ của riêng bạn

cho những người bạn đã quen thuộc với CSS, ý tưởng rất giống với cách sử dụng:

margin-left: 2px; 

thay vì phải thay đổi toàn bộ giá trị:

margin: 5px 5px 5px 2px; 
+1

Tự xem là UIView? Nếu vậy, thì thuộc tính frame không chỉ đọc. [Tài liệu] (https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816) nói rằng nếu bạn đặt , nó sẽ vẽ lại. Bạn sẽ có thể chỉ cập nhật một trường trong đó. – user1118321

Trả lời

86

Để trả lời xuất xứ của bạn al câu hỏi: có, nó có thể thay đổi chỉ là một thành viên của một cấu trúc CGRect. Mã này ném không có lỗi:

myRect.size.width = 50; 

là gì không thể, tuy nhiên, là để thay đổi thành viên duy nhất của một CGRect rằng chính nó là một tài sản của đối tượng khác. Trong trường hợp này rất phổ biến, bạn sẽ phải sử dụng một biến cục bộ tạm thời:

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

Lý do cho điều này là sử dụng các accessor tài sản self.frame = ... tương đương với [self setFrame:...] và accessor này luôn hy vọng toàn bộ CGRect. Việc pha trộn kiểu C struct truy cập với ký hiệu chấm đặc tính Objective-C không hoạt động tốt trong trường hợp này.

+1

@OleBegermann phần tốt nhất là bạn (43k) nói có và Milos (26) nói không. Tôi chỉ không biết phải làm gì! – Jacksonkr

+2

Như tôi đã nói, nó thường hoạt động nhưng nó không _not_ nếu 'CGRect' là thuộc tính của đối tượng Objective-C, như trong ví dụ của bạn. Milos có lẽ là đề cập đến mã mẫu của bạn và là chính xác trong vấn đề đó. –

+7

Chỉ cần đề cập đến điều này - 'self.frame.size.width = 50' hoạt động trong Swift. – ArtOfWarfare

4

Nếu bạn thấy mình cần phải làm điều này loại điều chỉnh thành phần cá nhân, nó có thể đáng giá có macro như thế này ở đâu đó truy cập bởi tất cả các mã của bạn:

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

Bằng cách này, bất cứ khi nào bạn cần phải thay đổi chiều rộng một mình - bạn chỉ cần viết

ViewSetWidth(self, 50); 

thay vì

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

Đề xuất rất hay, mặc dù tôi chỉ thêm vào CGRectSetWidth (cùng với setters cho chiều cao, x, và y.) Có vẻ như với tôi rằng việc trộn cú pháp CPP với cú pháp Obj-C sẽ có vẻ khó hiểu ... – ArtOfWarfare

26

Tôi thích Ahmed Khalaf's answer, nhưng nó xảy ra với tôi rằng bạn cũng có thể chỉ cần viết ra một vài chức năng C ... lợi thế chính là nó sẽ được dễ dàng hơn để theo dõi lỗi trong trường hợp bạn đang sử dụng sai kiểu.

Có nói rằng, tôi đã viết một file .h với những tuyên bố chức năng:

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

Và một file .m tương ứng với các chức năng hiện thực:

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

Vì vậy, bây giờ, để làm những gì bạn muốn, bạn chỉ có thể làm:

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

Nhận thậm chí Fancier (cập nhật tôi đã thực hiện một năm sau)

Điều này có dư thừa self.frame trong đó. Để khắc phục điều đó, bạn có thể thêm một category trên UIView với các phương pháp mà trông như thế này:

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

Và bây giờ bạn có thể chỉ cần gõ vào:

[self setFrameWidth:50]; 

Hoặc, thậm chí tốt hơn:

self.frameWidth = 50; 

Và chỉ để bạn có thể làm điều gì đó như sau:

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

Bạn sẽ cũng cần phải có điều này trong thể loại của bạn:

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

Thưởng thức.

+0

Như một phần thưởng bổ sung cho phương thức sử dụng #define của Ahmed ... nó đã được chỉ ra để tôi một hoặc hai tuần trước rằng '# define' khá là xấu xa. Nó không phải là bối cảnh nhạy cảm và vì vậy nếu bạn có một cái gì đó như '#define 8' của tôi, nó sẽ thay thế một cái gì đó như' myRect' với '8Rect' ... từ đó, tôi đã quyết định tôi sẽ không sử dụng' #define '. – ArtOfWarfare

+0

Có thời gian và địa điểm cho #define, nhưng vì lý do bạn đã đề cập, chỉ cần chọn tên định nghĩa được đảm bảo là duy nhất trong mã của bạn, ví dụ: '#define MY_SUBCLASS_LEFT_MARGIN 10' –

+2

@CarlosP No.' const int MY_SUBCLASS_LEFT_MARGIN = 10; '- lần duy nhất bạn nên sử dụng #define là cho một cái gì đó chỉ có thể được biết tại thời gian biên dịch, ví dụ, chi tiết về trình biên dịch hoặc phiên bản đang được biên dịch có thể được định nghĩa. Giả sử bạn đang làm việc trên một dự án lớn và một ai đó ở nơi khác có '#define MARGIN 8'. Bây giờ định nghĩa của bạn bị hỏng và bản dựng không thành công. 'const' đã được thêm vào vì một lý do, và tôi cần phải bash nó vào rất nhiều lập trình viên C, C++ và Obj-C, vì một số lý do vì chúng quyết tâm sử dụng sai' # define' của CPP. – ArtOfWarfare

5

Dựa trên ArtOfWarfare's solution (điều này thực sự tuyệt vời) Tôi đã tạo danh mục UIView không có chức năng C.

ví dụ sử dụng:

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

Tiêu đề tập tin 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; 

tập tin thực hiện 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; 
} 
1

Dựa trên câu trả lời n0_quarter, đây là một mở rộng UIView viết bằng 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) 
    } 
} 
+1

Vì vậy, điều này được dựa trên n0_quarter's, được dựa trên của tôi, được dựa trên arkuana của. Câu trả lời giờ đã chuyển từ CPP sang C thành Obj-C thành Swift. – ArtOfWarfare

2

Trong Swift bạn có thể mở rộng các lớp UIView cho các ứng dụng, như vậy mà bạn có thể, ví dụ, di chuyển đầu bảng xem tăng 10 điểm như thế này:

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

(tất nhiên nếu bạn đang sử dụng AutoLayout, điều đó có thể không thực sự làm bất cứ điều gì ...:))

Bằng cách mở rộng UIView (ảnh hưởng đến mỗi UIView và lớp con của nó trong ứng dụng của bạn)

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

Hope Tôi không phải là quá muộn để đảng, đây là giải pháp của tôi trong ObjC.

Đối với tôi, tôi thích gắn bó với một chức năng duy nhất thay vì một số chức năng và tôi thích cách tiếp cận CSS được áp phích bởi người đăng. Dưới đây là chức năng tôi thêm vào danh mục UIView của tôi.

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

Được hiển thị trong ví dụ, nó xem xét từng giá trị trong CGR được cung cấp và nếu giá trị lớn hơn 0, có nghĩa là chúng tôi muốn thay thế giá trị đó. Và do đó, bạn có thể làm CGRect (0,0,100,0) và nó sẽ giả định chúng tôi muốn thay thế chiều rộng chỉ.

0

thường xuyên gọi phương thức setFrame không phải là quá tốt, như: setFrameX(x) setFrameY(y) setFrameWidth(width) ...

thay vào đó, trong nhanh chóng, chúng tôi có thể tận dụng các thông số mặc định để thiết lập nhiều thông số duy nhất trong một cuộc gọi:

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

sau đó cập nhật thông số của khung chỉ cần:

setFrame(x: x, width: min(maxX - x, titleLabel.bounds.size.width)) 
setFrame(width: titleDescriptionLabel.bounds.size.width + 5, height: titleDescriptionLabel.bounds.size.height + 6) 
Các vấn đề liên quan