所以我有一个观点,我在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
)
如果我需要发布堆栈中使用的方法的代码,请告诉我这是有用的。 (对不起,我以前从未真正处理过这些事情)
答案 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 {};