Проблема с CGAffineTransform при сохранении NSView в pdf файл


#1

Добрый день!
Уже два дня пытаюсь найти решение следующей проблемы возникшей при сохранении NSView в pdf файл а также при его печати. Данный NSView содержит как subviews NSImageView и NSTextFields. Эти subviews юзер добавляет к NSView и может их вращать. Для этого используется следующий код.

func rotateView(angle: CGFloat)
{
    self.layer!.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
    self.layer!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    self.layer!.transform = CATransform3DMakeAffineTransform(CGAffineTransform(rotationAngle: angle))
}

Проблема состоит в том что если печатать или сохранять этот NSView как pdf файл то NSImageView и NSTextFields, к которым была применена эта функция, не повернуты, т.е. на экране повернуты а при печати и в pdf все повороты отсутствуют.

PS.
Та же проблема возникает если у добавленных NSImageView и NSTextFields как subviews изменить альфа фактор, или вместо использованной функции для поворота использовать переменную - myNsview.frameCenterRotation = angle. Все эти изменения not applied если сохранять в файл или печатать.


#2

Запостил тот же вопрос на stackoverflow, спросили код, добавил, но опять никто ничего не советует. Что то везет мне последнее время на проблемы без решений.


#3

Это пробовали
https://developer.apple.com/documentation/uikit/uiview/1622531-snapshotview


#4

Нет, не пробовал, для NSView нет много, что есть для UIView.
Обходной путь есть, с использованием bitmapImageRepForCachingDisplay(in:) и cacheDisplay(in:to:) для этого NSView и переносом всего этого добра в NSImageView который и распечатывается.
Но, тогда этот pdf как картинка, текст если добавлялся в NSView как NSTextField тоже картинка и его качество ниже если бы это был натуральный pdf текст.

Поэтому лучше как то напрямую решить проблему и заставить все эти трансформации subview в NSView отображаться в pdf. Должно же быть какое-то решение, со всеми этими слоями и их трансформациями. Пробовал играть с удалением и добавлением слоев с поворотами в draw

override func draw( _ dirtyRect: NSRect)
{
      super.draw(dirtyRect)

        let context = NSGraphicsContext.current
        if context!.isDrawingToScreen
        {
        }
        else
        {
          self.layer!.render(in: context!.cgContext)
        }
}

Но безрезультатно.


#5

Еще одна попытка использовать isDrawingToScreen . Результат - неясный. Может кто увидит что надо исправить чтобы все заработало.

Итак, в кастомном NSView имеем

 override func draw( _ dirtyRect: NSRect)
{
      super.draw(dirtyRect)

        let context = NSGraphicsContext.current

        if context!.isDrawingToScreen
        {

        }
        else
        {

            if self.subviews.isEmpty != true
            {
                  self.subviews.removeAll()
            }

            for each in arrayOfNSImageViews
           {
                self.layer?.addSublayer(each.0.layer!) // each.0 - NSImageView 
                each.0.rotateView(angle: each.1) // each.1 - угол поворота сохраненный в tuple вместе с добавленным NSImageView при повороте.   
           }
                self.layer!.render(in: context!.cgContext)
       }

}

Предполагается в том случае если имеем печать то сначала удаляем все subview потом добавляем слои этих subview в слой NSView и поворачиваем их, затем используем render чтобы записать все слои в context .
Результат следующий. Если делаю print в preview теперь выглядит как и должно, поворот есть. Но, сохранить в pdf файл не получается, ошибка выскакивает на render. Если не сохранять в файл a сделать cancel print картинка в NSView становится пустой. Но, cтранно то что после того как еще раз нажать в меню print картинка в NSView возвращается и в print preview тоже видим картинку с поворотом. Опять же при попытке сохранить в pdf вылетает ошибка на render ( EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8) )


#6

Нашел ответ :slight_smile:

Проект с проблемами - это document based проект для мака и с использованием XIB файла, не stroryBoard.

В document based проекте как default есть Document.swift и Document.xib. Document.swift содержит windowsController и соответвенно windowsController содержит ContentViewController.

Весь UI можно делать в Document.xib а код для него писать в Document.swift. Можно и read и data функции нормально использовать. Для read данные использовать в windowControllerDidLoadNib, присваивать данные после того как интерфейс уже на месте.

Но, все чудеса с поворотами и печатью повернутых subviews возникают именно с таким образом организованным проектом.

Если отказаться от дефолтного windowsController, сделать новый windowsController с использованием makeWindowControllers() , сделать отдельный ViewController c XIB файлом и заменить дефолтный Document.xib на XIB от ViewController, ну и ViewController сделать как contentViewController, то весь маразм с поворотами subviews и печатью исчезает.

Сопутствующий вопрос. А зачем нужен дефолтный windowsController если все равно нужно его удалять и писать свой ? Удобнее было бы если бы по дефолту уже был отдельный viewController как contentViewController и его XIB.