NSScrollView не отображает вид документа?

Я создал собственное представление, которое реализует initWithFrame и drawRect. В IB я создал окно и поместил пользовательский вид в центр окна. Затем я использовал Editor>Embed In>Scroll View, чтобы поместить пользовательское представление в представление прокрутки. А потом ничего? Сообщение initWithFrame отправляется пользовательскому представлению, но сообщение drawRect никогда не отправляется.

Подобное поведение может быть создано без моего специального пользовательского представления. Если обертывающая метка будет встроена в вид прокрутки таким же образом, она также не будет отображаться.

Как заставить вид прокрутки отрисовывать его содержимое?

Кроме того, в несколько связанной заметке, каков правильный способ программного изменения размера представления, встроенного в прокрутку?

Спасибо!


person user1574591    schedule 04.01.2014    source источник
comment
взгляните на stackoverflow.com/questions/19663776/   -  person Yrogirg    schedule 14.01.2014


Ответы (3)


NSScrollView не похож на другие представления. Он делает вещи настолько по-разному, что поначалу всех сводит с ума, особенно после того, как вы привыкли к мышлению Auto Layout. Это также одно из тех представлений (наряду с NSTableView, NSOutlineView, NSCollectionView и другими), где нужно настроить множество частей, прежде чем вы сможете увидеть, как все работает. Auto Layout также немного добавляет к этому.

NSScrollView по умолчанию содержит как минимум 3 подпредставления:

  • Один объект NSClipView. (Apple называет это Content View. Это то, что видно.)
  • Два объекта NSScroller.

Объект NSClipView или Content View содержит некоторый вид NSView.

(Apple называет это представлением документа)

Если вы добавите простой NSScrollView в свой xib из библиотеки объектов, он будет иметь размер в Interface Builder, но во время выполнения он будет иметь нулевой размер. Это будет бить вас по заднице каждый раз. Вы можете увидеть это, добавив запись в журнал.

NSLog(@" size of document view:%@", NSStringFromRect(_myScrollView.documentView.frame);

Даже если вы добавите подпредставление и будете звонить setNeedsDisplay:YES весь день, вы ни хрена не увидите.

Однако есть вещи, которые вы можете сделать. Во-первых, вы можете установить размер documentView с помощью setFrameSize:NSMakeSize(somePointsWide, somePointsHigh)

Вы также можете сделать его настраиваемым подклассом NSView, который имеет реализацию intrinsicContentSize, которая вычисляет это на основе любых логических правил, которые вы хотите поместить туда (включая некоторый минимум по умолчанию).

Вы также можете просто сказать, что все подпредставления должны быть на расстоянии не менее N точек от documentView или более на каждом краю с помощью автоматического макета. VFL (язык визуального формата) будет выглядеть так: @"H:|-nPoints-[aSubviewOfDocumentView]-nPoints-|" и этот @"V:|-nPoints-[aSubviewOfDocumentView]-nPoints-|" (убедитесь, что ваши подпредставления имеют внутренний размер или какое-то собственное подпредставление, которое дает им минимальный размер... и реализует - (BOOL)translatesAutoresizingMaskIntoConstraints { return NO; } и отключает автоматическое изменение размеров подпредставлений для ваш documentView с помощью флажка в IB или вызовом setAutoResizesSubviews:NO или вы будете ненавидеть жизнь.)

Теперь это приводит к странной вещи. С Auto Layout, как правило, хорошо перестать думать о фреймах и прямоугольниках, насколько это возможно, но с NSScrollView documentView вы можете рассмотреть возможность не использовать Auto Layout для позиционирования подпредставлений. В некоторых случаях может быть проще не делать этого и просто позволить documentView расти, чтобы приспособиться. Если вы используете описанный выше подход «nPoints», и ваши подпредставления можно перетаскивать, вы хотите просто захватить NSPoint для начала кадра подпредставления (используйте его для определения вашей дельты), а затем точку mouseDown и, когда перетаскивание завершено, захватить мышь точка и рассчитайте дельту для новой точки начала кадра. Затем вызовите setFrameOrigin: с новой точкой.

person uchuugaka    schedule 30.01.2014
comment
Я могу забрать немного о innerContentSize , я возился с этим с момента публикации вопроса, и я действительно чувствую, что он полностью сломан в режиме автоматического макета. - person uchuugaka; 03.02.2014
comment
Спасатель жизни. Я уже собирался поставить новый вопрос о, но нашел это. - person LiMar; 19.02.2014

Используете ли вы автомакет или старомодное изменение размера представления? Если вызывается ваш метод инициализации, но не метод рисования, это звучит так, как будто весь ваш scrollView или ваш пользовательский вид имеют нулевой размер.

Если у вас есть настраиваемое представление и вы используете автомакет, реализовали ли вы метод -intrinsicContentSize в своем подклассе настраиваемого представления?

person Wil Shipley    schedule 06.01.2014
comment
В Xcode 7.3.1 Interface Builder заявляет следующее, когда вы выбираете параметр «заполнитель» в Intrinsic Size: Установка внутреннего размера содержимого во время разработки влияет только на представление при редактировании в Interface Builder. Представление не будет иметь этот внутренний размер содержимого во время выполнения. - person Elise van Looij; 30.08.2016
comment
Да, я говорил о добавлении внутреннего размера контента в код, а не в IB. Таким образом, «реализуйте метод -intrinsicContentSize». Это отличается от одноименной функции IB. - person Wil Shipley; 04.09.2016
comment
@WilShipley прав, метод innerContentSize позволяет вам делать любой ответ с условным размером, который вам нужен. - person uchuugaka; 12.09.2016

Я только что закончил борьбу, уговаривая NSScrollView работать с пользовательским представлением. Как указал Учуугака несколько лет назад, изменение размера NSScrollView с включенной системой автоматической компоновки может привести к тому, что представление прокрутки увеличит границы вашего пользовательского представления, когда представление прокручиваемого содержимого больше, чем ваше фактическое представление документа.

Есть две вещи, на которые стоит обратить внимание, и обе можно исправить в drawRect() вашего customView.

  1. Когда окно снова уменьшится, расширенный вид документа не уменьшится вместе с ним, а полосы прокрутки будут иметь неправильный размер.
  2. Иногда при резком изменении размера область пользовательского представления становится немного больше, чем фактический документVisibleRect. Это ошибка. Как только это произойдет, независимо от того, насколько сильно вы перетащите прокрутку, полосы прокрутки никогда не исчезнут.

Чтобы бороться с этим, мой пользовательский вид (который прокручивается только по вертикали) проверяет эти случаи в drawRect() и корректирует размеры в зависимости от того, увеличились мы или уменьшились.

 if scrollView.documentVisibleRect.size.height >= actualDocumentHeight {
        //
        // Sometimes the scroller machinery can enlarge the document rect a little
        // extra when resizing the window quickly.  We should check for that.
        //
        if self.bounds.size.height > scrollView.contentSize.height {
            self.frame.size.height = scrollView.contentSize.height
            self.bounds.size.height = scrollView.contentSize.height
        }
    } else {
        //
        // If it's smaller, set our size to the actual size and the scroll
        // bars will pop on.
        //
        self.frame.size.height = actualDocumentHeight
        self.bounds.size.height = actualDocumentHeight
    }
person Jim Hayes    schedule 22.09.2019