2014-02-18 75 views
3

所以我有一種觀點,我在IB中設置,我需要以編程方式更改框架。出於某種原因,框架在設置完成後會不斷恢復到其IB位置。我將NSView分類並在-setFrame:(NSRect)frameRect方法中記錄了框架,它看起來像-setFrame:正在調用兩次 - 一次當我設置它(記錄新值)和一次它恢復時(它記錄IB值)。我似乎無法提煉出問題的根源,因爲在某些情況下(例如,如果我有一個NSButton專門設置它或者有一個定時器設置框架),它可以很好地工作,但如果我在線調用-setFrame:與我的其他代碼,它總是恢復。編程設置後NSView框恢復

編輯:

這是一個簡單的例子,示出了該問題(在IB原始幀是{{20,118},{48,48}}):

AppDelegate.m :

#import "AppDelegate.h" 

@implementation AppDelegate 

- (void)awakeFromNib{ 
    [self.button setFrame:NSMakeRect(50, 10, 100, 100)]; 
} 

@end 

登錄:

2014-02-18 18:01:40.206 WHS-ChangingFrameTest[15210:303] Frame: {{50, 10}, {100, 100}} 
2014-02-18 18:01:41.223 WHS-ChangingFrameTest[15210:303] Frame: {{20, 118}, {48, 48}} 

編輯#2:

調用堆棧當我從編輯框(從原來的應用程序):

0 MyApp     0x000000010000203c -[FrameLogProgressIndicator setFrame:] + 284 
1 MyApp     0x000000010001c994 -[SubjectViewController updateTableViewHeight] + 1284 
2 MyApp     0x000000010001c468 -[SubjectViewController updateUI] + 4664 
3 MyApp     0x0000000100012f2f -[TabMenuViewController updateDisplayingBlock:] + 975 
4 MyApp     0x0000000100010c59 -[TabMenuViewController switchBlockFromDaySchedulePopover:] + 873 
5 AppKit        0x00007fff82eea959 -[NSApplication sendAction:to:from:] + 342 
6 AppKit        0x00007fff82eea7b7 -[NSControl sendAction:to:] + 85 
7 AppKit        0x00007fff82eea6eb -[NSCell _sendActionFrom:] + 138 
8 AppKit        0x00007fff82ee8bd3 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 1855 
9 AppKit        0x00007fff82ee8421 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 504 
10 AppKit        0x00007fff82ee7b9c -[NSControl mouseDown:] + 820 
11 AppKit        0x00007fff82edf50e -[NSWindow sendEvent:] + 6853 
12 AppKit        0x00007fff82edb644 -[NSApplication sendEvent:] + 5761 
13 AppKit        0x00007fff82df121a -[NSApplication run] + 636 
14 AppKit        0x00007fff82d95bd6 NSApplicationMain + 869 
15 MyApp     0x00000001000020a2 main + 34 
16 libdyld.dylib      0x00007fff8152a7e1 start + 0 
17 ???         0x0000000000000003 0x0 + 3 
) 

呼叫從當框架協議棧是恢復回:

0 MyApp     0x000000010000203c -[FrameLogProgressIndicator setFrame:] + 284 
1 AppKit        0x00007fff82e21e77 -[NSView resizeWithOldSuperviewSize:] + 659 
2 AppKit        0x00007fff82e21307 -[NSView resizeSubviewsWithOldSize:] + 318 
3 AppKit        0x00007fff82f08399 NSViewLevelLayout + 44 
4 AppKit        0x00007fff82f07e65 -[NSView _layoutSubtreeHeedingRecursionGuard:] + 112 
5 CoreFoundation      0x00007fff84b524a6 __NSArrayEnumerate + 582 
6 AppKit        0x00007fff82f07fc6 -[NSView _layoutSubtreeHeedingRecursionGuard:] + 465 
7 CoreFoundation      0x00007fff84b524a6 __NSArrayEnumerate + 582 
8 AppKit        0x00007fff82f07fc6 -[NSView _layoutSubtreeHeedingRecursionGuard:] + 465 
9 CoreFoundation      0x00007fff84b524a6 __NSArrayEnumerate + 582 
10 AppKit        0x00007fff82f07fc6 -[NSView _layoutSubtreeHeedingRecursionGuard:] + 465 
11 CoreFoundation      0x00007fff84b524a6 __NSArrayEnumerate + 582 
12 AppKit        0x00007fff82f07fc6 -[NSView _layoutSubtreeHeedingRecursionGuard:] + 465 
13 AppKit        0x00007fff82f07cfe -[NSView layoutSubtreeIfNeeded] + 615 
14 AppKit        0x00007fff82f034ac -[NSWindow(NSConstraintBasedLayout) layoutIfNeeded] + 201 
15 AppKit        0x00007fff82dfd0a8 _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints + 446 
16 AppKit        0x00007fff833c8901 __83-[NSWindow _postWindowNeedsDisplayOrLayoutOrUpdateConstraintsUnlessPostingDisabled]_block_invoke_01208 + 46 
17 CoreFoundation      0x00007fff84b20417 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23 
18 CoreFoundation      0x00007fff84b20381 __CFRunLoopDoObservers + 369 
19 CoreFoundation      0x00007fff84afb7b8 __CFRunLoopRun + 728 
20 CoreFoundation      0x00007fff84afb0e2 CFRunLoopRunSpecific + 290 
21 HIToolbox       0x00007fff8231aeb4 RunCurrentEventLoopInMode + 209 
22 HIToolbox       0x00007fff8231ab94 ReceiveNextEventCommon + 166 
23 HIToolbox       0x00007fff8231aae3 BlockUntilNextEventMatchingListInMode + 62 
24 AppKit        0x00007fff82dfa533 _DPSNextEvent + 685 
25 AppKit        0x00007fff82df9df2 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128 
26 AppKit        0x00007fff82df11a3 -[NSApplication run] + 517 
27 AppKit        0x00007fff82d95bd6 NSApplicationMain + 869 
28 MyApp     0x00000001000020a2 main + 34 
29 libdyld.dylib      0x00007fff8152a7e1 start + 0 
30 ???         0x0000000000000003 0x0 + 3 
) 

讓我知道是否需要從堆棧中使用的方法發佈代碼,以便有用。 (對不起,我從來沒有真正處理了這個東西之前)

+0

請發佈一個調用堆棧,其中setFrame被調用錯誤的數據 – Avt

+0

大概你是從錯誤的地方調用你的幀更新。但是你沒有向我提供額外的信息。所以我不能幫你。 – Avt

+0

改變幀的含義是什麼?它的大小,位置? -1 –

回答

1

所以我設法通過繼承他們停下來的意見調整大小和壓倒一切的-resizeWithOldSuperviewSize:只是什麼也不做,就像這樣:

- (void)resizeWithOldSuperviewSize:(NSSize)oldSize {}; 
2

從awakeFromNib描述:

由於其對象從歸檔中實例化的順序是不能保證,你的初始化方法應該不發送消息給其他對象在層次結構中。到其他對象的消息可以從awakeFromNib方法中安全地發送。 通常,您需要爲需要額外設置的對象實現awakeFromNib,而這些對象在設計時無法完成。例如,您可以使用此方法自定義任何控件的默認配置,以匹配用戶首選項或其他控件中的值。您也可以使用它將單個控件恢復到應用程序的某個以前的狀態。

我不是100%肯定,但我強烈建議移動

[self.button setFrame:NSMakeRect(50, 10, 100, 100)]; 

- (void)viewDidLoad 

方法。你也不應該忘記調用super的方法 - 在某些情況下,它可能很重要。因此,最終的代碼應該是這樣的:

@implementation AppDelegate 

- (void)awakeFromNib{ 
    [super awakeFromNib]; 
    ... non GUI initialization 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self.button setFrame:NSMakeRect(50, 10, 100, 100)]; 
} 

@end 

UPDATE: 感謝電話卡。看起來你的觀點是自動化的。您應該檢查autoresizingMask自動佈局約束

+0

謝謝,但我不認爲這解決了我的問題,因爲在更大的應用程序中,我正在開發返回,即使在'-setFrame:'從'-awakeFromNib'之外的地方調用時也會發生。我用我原來的應用程序的調用堆棧更新了我的問題。 –

+0

感謝致電卡住。看起來你的觀點是自動化的。你應該檢查autoresizingMask和自動佈局的約束條件 – Avt

+0

我試着將autoreziseMask設置爲NSViewNotSizeable(這似乎沒有效果)並且擺脫了所有約束條件(它將初始幀設置爲與IB完全不同的東西,但是我還試着將superview的'-setAuotresizesSubviews:'設置爲NO,這也沒有做任何事情。你有什麼想法可以阻止視圖的autoresizing?autoresizingMask返回12,唯一的約束是:

3

的替代更換內容resizeWithOldSuperviewSize:是通知自動佈局系統,你不希望你的NSView被調整大小。這將使您的NSView保持在您以編程方式指定的原點,從而保持您對接口生成器的覆蓋不變。

[<id> setAutoresizingMask:NSViewNotSizable]; 
[<id> setTranslatesAutoresizingMaskIntoConstraints:YES]; 

其中<id>將是你的NSView的實例,即self.button:您可以通過做到這一點。第一行指出視圖不是很大,而第二行指出掩碼應該被自動佈局系統視爲約束。然後,您的修訂AppDelegate.m將是:

#import "AppDelegate.h" 

@implementation AppDelegate 

- (void)awakeFromNib{ 
    [self.button setAutoresizingMask:NSViewNotSizable]; 
    [self.button setTranslatesAutoresizingMaskIntoConstraints:YES]; 
    [self.button setFrame:NSMakeRect(50, 10, 100, 100)]; 
} 

@end 

更新: 如果您使用此方法,你打算設置hidden一個的NSView,自動佈局系統將仍然需要隱藏的框架調整框架的超級視圖/窗口大小時考慮NSView。這意味着如果隱藏的NSView在調整大小之後會在超級視圖的可見區域之外,自動佈局系統將阻止超級視圖正確調整大小,而是強制超級視圖框架將隱藏的NSView包圍起來。

一種Na ï已經解決了這個問題是設置hidden:YES和設置hidden:NO之前恢復的寬度和高度後到的NSView的寬度和高度設置爲零。例如,在代碼中的某個點使用的NSView self.button

... 
[self.button setHidden:YES]; 
[self.button setFrameSize:NSZeroSize]; 
... 

及更高版本:

... 
[self.button setFrameSize:NSMakeSize(160, 90)]; 
[self.button setHidden:NO]; 
... 

不過,如果你對一個NSView設置(以編程方式或寬度/高度自動佈局限制通過界面生成器),這些變化可能會引發類似的警告:

Unable to simultaneously satisfy constraints: 
(
    "<NSLayoutConstraint:0x608000082990 H:[NSButton:0x6080001200a0(100)]>", 
    "<NSAutoresizingMaskLayoutConstraint:0x60800008bae0 h=--& v=--& H:[NSButton:0x6080001200a0(0)]>" 
) 

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x608000082990 H:[NSButton:0x6080001200a0(100)]> 

您可以忽略這些警告,約束優先級降低到低號,從=約束更改爲,或交替您可以隱藏的NSView,並將其設置爲YES之前之前只是setTranslatesAutoresizingMaskIntoConstraints:NO來取消隱藏所的NSView:

... 
[self.button setTranslatesAutoresizingMaskIntoConstraints:NO]; 
[self.button setHidden:YES]; 
... 

當我們取消隱藏的NSView:

... 
[self.button setTranslatesAutoresizingMaskIntoConstraints:YES]; 
[self.button setHidden:NO]; 
... 

您也可以通過繼承的NSView和壓倒一切的setHidden:(注意!)自動完成:

- (void)setHidden:(BOOL)hidden { 
    [self setTranslatesAutoresizingMaskIntoConstraints:!hidden]; 
    [super setHidden:hidden]; 
} 

然後您可以簡單地致電[self.button setHidden:YES];[self.button setHidden:NO];,並且重寫的方法將處理所有內容。