以编程方式设置后的NSView帧恢复

时间:2014-02-18 23:34:42

标签: objective-c cocoa interface-builder frame nsview

所以我有一个观点,我在IB中设置,我需要以编程方式更改帧。由于某种原因,框架在我设置之后不断恢复到其IB位置。我将NSView子类化并在-setFrame:(NSRect)frameRect方法中记录了框架,看起来-setFrame:被调用两次 - 一次设置它(它记录新值)和一次恢复(其中)它记录IB值)。我似乎无法提炼问题的根源,因为在某些情况下(例如,如果我有一个专门用于设置它的NSButton或者设置一个定时器设置框架)它可以完美地工作,但是如果我有-setFrame:在线调用我的其他代码,它总是会恢复。

编辑:

这是一个显示问题的简单示例(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
)

如果我需要发布堆栈中使用的方法的代码,请告诉我这是有用的。 (对不起,我以前从未真正处理过这些事情)

3 个答案:

答案 0 :(得分: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将位于superview的可见区域之外,则自动布局系统将阻止superview正确调整大小,而是强制superview框架包含隐藏的NSView。

解决此问题的一个简单方法是在设置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上设置了宽度/高度自动布局约束(通过编程方式或通过Interface Builder),则这些更改可能会出现如下警告:

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

您可以忽略这些警告,将约束优先级降低到较低的数字,将约束从 = 更改为,或者您可以简单地{{1}隐藏NSView之前,在取消隐藏NSView之前将其设置为setTranslatesAutoresizingMaskIntoConstraints:NO

YES

当我们取消隐藏NSView时:

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

您也可以通过继承NSView并覆盖... [self.button setTranslatesAutoresizingMaskIntoConstraints:YES]; [self.button setHidden:NO]; ... 来自动化(注意setHidden:):

!

然后,您只需致电- (void)setHidden:(BOOL)hidden { [self setTranslatesAutoresizingMaskIntoConstraints:!hidden]; [super setHidden:hidden]; } [self.button setHidden:YES];,重写的方法即可完成所有操作。

答案 1 :(得分:2)

来自awakeFromNib说明:

由于无法保证从归档实例化对象的顺序,因此初始化方法不应将消息发送到层次结构中的其他对象。可以从awakeFromNib方法中安全地发送到其他对象的消息。 通常,您需要为需要在设计时无法完成的其他设置的对象实现awakeFromNib。例如,您可以使用此方法自定义任何控件的默认配置,以匹配用户首选项或其他控件中的值。您也可以使用它将单个控件恢复到应用程序的某个先前状态。

我不是100%肯定,但我强烈建议移动

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

- (void)viewDidLoad

方法。此外,你不应该忘记调用超级方法 - 在某些情况下,它可能是至关重要的。所以你的最终代码应如下所示:

@implementation AppDelegate

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

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

@end

更新: 谢谢你的电话卡住了。您的视图似乎已自动调整。您应该检查autoresizingMask和自动布局约束

答案 2 :(得分:1)

所以我设法通过继承它们并覆盖-resizeWithOldSuperviewSize:来阻止视图的大小调整,就像这样做:

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