2014-02-18 2 views
3

Итак, у меня есть представление, которое я установил в IB, и мне нужно программно изменить кадр. По какой-то причине рамка продолжает возвращаться в свое местоположение IB после того, как я ее установил. Я подклассифицировал NSView и зарегистрировал фрейм в методе -setFrame:(NSRect)frameRect, и он выглядит так: -setFrame: получает вызов дважды - один раз, когда я его устанавливаю (где он регистрирует новые значения) и один раз, когда он возвращается (где он регистрирует значения IB). Я не могу отличить корень проблемы, потому что в некоторых ситуациях (например, если у меня есть NSButton, посвященный настройке или настройке таймера кадра), он отлично работает, но если у меня есть вызов -setFrame: в строке с моим другим кодом, он всегда возвращается.NSView Frame Reverting после того, как он был установлен программно

Редактировать:

Это простой пример, который показывает проблему (исходный кадр в 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}} 

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

метод. Также вы не должны забывать о методах супер - в некоторых случаях это может быть критическим. Так что ваш окончательный код должен выглядеть следующим образом:

@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, но я также попытался установить «-setAuotresizesSubviews» супервизора: «NO», и ничего не сделал. Есть ли у вас какие-либо идеи о том, как я могу остановить просмотр из авторезистации? AutoresizingMask возвращает 12 и единственное ограничение is '' –

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 

Update: Если вы используете этот метод с NSView, который планируется установить hidden, система автоматического макета будет еще кадр скрытый NSView учитывается при изменении размера окна просмотра/окна рамки. Это означает, что если скрытый NSView будет находиться за пределами видимой области супервизора после изменения размера, система автоматического макета будет препятствовать правильному изменению размера супервизора и вместо этого заставит рамку супервизора заключить скрытый NSView.

Один на ï ве решения этой проблемы состоит в установке ширины и высоту NSView до нуля после установки hidden:YES и восстановления ширины и высоты перед установкой hidden:NO. Например, в какой-то момент в коде с помощью 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)]> 

Вы можете либо игнорировать эти предупреждения, уменьшить приоритет ограничений, до низкого уровня номер, изменить ограничение от = к , или поочередно, вы можете просто setTranslatesAutoresizingMaskIntoConstraints:NO прежде, чем прятать NSView, и установив его на YES До восстановления скрытого в 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];, а переопределенный метод позаботится обо всем.

Смежные вопросы