IBDesignable from External Framework?

Я хотел бы создать некоторые пользовательские представления, которые используют теги @IBDesignable и @IBInspectable. Я добавил их в свою Framework, а затем связал мою Framework с моим тестовым приложением. Но Designables никогда не появляются в StoryBoard.

Как я могу использовать @IBDesignable и @IBInspectable для создания пользовательских представлений из внешней структуры?

Можете ли вы использовать @IBDesignable и @IBInspectable в приложении из не-внедренной Framework?

Спасибо.

Ответ 1

Я нашел способ использования конструктивных элементов и проверок с помощью Cocoa Touch frameworks. Ниже приведены инструкции для проекта Objective-C и Xcode 8 (я не тестировал более старые версии) и должен быть идентичным, если задействован код Swift.

Так как конструкторы не обнаружены интерфейсом Builder в фреймворках, бесполезно отмечать классы как IB_DESIGNABLE в заголовках фреймов. При компиляции исходных файлов проекта Interface Builder будет обнаруживать только назначаемые классы. Поэтому идея состоит в том, чтобы предоставить эту информацию в качестве исходного файла сопутствующей среды, которую клиенты могут затем скомпилировать с помощью своего проекта.

Я обнаружил, что вам не нужно подклассом отмечать класс framework как назначаемый в проекте. Вы можете просто аннотировать каждый класс, который должен быть назначаемым через категорию, указанную в исходном файле .m для компаньона, например:

IB_DESIGNABLE
@interface MyCustomView (Designable)

@end

На самом деле, код даже не нужно компилировать, вы можете обернуть его в закрывающий #if 0 ... #endif, и он все равно будет работать. Все, что необходимо, это то, что класс каким-то образом связан с атрибутом IB_DESIGNABLE.

С учетом этой информации, вот как сделать конструктивные элементы работы с Cocoa Touch frameworks:


Если вы поставщик инфраструктуры:

  • Если необходимо, укажите компонент, который должен быть предназначенным для реализации -prepareForInterfaceBuilder
  • Добавьте ссылку на папку (синяя папка) в целевую среду фреймворка с помощью сопутствующего файла .m. Возможным соглашением об именах было бы назвать папку Designables и файл внутри нее MyFrameworkNameDesignables.m, но вы можете выбрать то, что вам больше всего нравится.
  • В файле .m создайте категорию, подобную приведенной выше для каждого представления, которое должно быть условным. Сам файл должен быть скомпилирован по клиентским проектам, что означает, что вам нужно либо сделать необходимые импорт (например, глобальный публичный заголовок фрейма #import <MyFramework/MyFramework.h>), либо использовать тэг #if 0 ... #endif выше

Приложив файл в синюю папку, мы гарантируем, что папка будет скопирована как в конечном .framework продукте без компиляции исходного файла-компаньона. Более того, поскольку папка является частью пакета framework, она доступна для всех клиентов фреймворка, независимо от того, интегрируют ли они его напрямую или используя Карфаген.

Если у вас есть демонстрационный проект, использующий структуру как целевую зависимость, и если ваша структура зависит от других фреймворков, вы столкнетесь с проблемами dlopen при попытке визуализации назначаемых представлений в демонстрационном проекте. Это связано с тем, что атрибуты IB_DESIGNABLE обнаруживаются в целевой среде фреймворка (поскольку к ней добавлена ​​папка Designables), которую Xcode предварительно создает в папке с производными данными Build/Intermediates/IBDesignables, соответствующей вашему проекту. Если вы посмотрите на содержимое этой папки, не будут найдены зависимости структуры, что приведет к проблемам dlopen.

Чтобы исправить рендеринг в вашей демонстрации, просто добавьте фазу "Копировать файлы" в целевую среду фреймворка, добавьте каждую требуемую зависимость от структуры в список файлов и установите каталог "Продукты" в качестве адресата. Теперь, когда Xcode создает вашу демонстрационную версию для рендеринга, она также будет включать в себя зависимости.


Если вы являетесь пользователем фреймворка с конструктивной поддержкой:

  • Добавьте фреймворк (и все его зависимости от структуры, если они есть), как встроенный бинарный объект для вашей цели.
  • Извлеките исходный файл-компаньон из пакета структуры и скопировать его в свой проект, добавив его в свою цель. Добавление файла, расположенного внутри рамки или с использованием символической ссылки, не работает, поскольку Xcode вообще не выглядит внутри фреймворков
  • Добавьте пример расклассифицированного класса представления (MyCustomView в нашем примере выше). Интерфейс Builder должен построить проект и отобразить представление

Это решение не идеально, поскольку вам все равно придется вручную копировать поставляемый исходный файл, который может меняться между версиями каркаса. Но он работает очень хорошо, и обеспечивает все необходимое в самом пакете.

Ответ 2

У меня есть обходное решение. С этим решением вам не нужно добавлять фрейм как цель. Поэтому он работает с Carthage.

@IBDesignable
class MyCustomView: CustomView {

    @IBInspectable override var bgColor: NSColor {
        get {
            return super.bgColor
        }
        set {
            super.bgColor = newValue
        }
    }
}

Создайте подкласс класса (MyCustomView) настраиваемого класса (CustomView) в проекте-получателе (не в проекте framework) и пометьте подкласс как @IBDesignable. Используйте подкласс в своем приложении. Этот способ делает работу @IBDesignable.

Внутри подкласса переопределить эти @IBInspectable свойства (bgColor), таким образом делает работу @IBInspectpect.

Вы можете столкнуться с этой проблемой: Загрузка кода из рамки для настраиваемого элемента управления в IBDesignable Следуя этому руководству, чтобы решить эту проблему: http://www.dribin.org/dave/blog/archives/2009/11/15/rpath/

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

Пожалуйста, оставляйте комментарии, если вы не можете заставить их работать.

Ответ 3

Хорошо, поэтому, если вы хотите включить @IBDesignabl e и @IBInspectable в framework, то framework должно быть:

  • Включено в приложение для потребления, поэтому framework не будет в нем собственного проекта. (т.е. добавление фреймворка в качестве цели, выполняя что-то вроде 'file -> new.. -> target -> framework' из приложения-потребителя).
  • Включите external framework как CocoaPod в ваше приложение-потребитель. Это фактически добавляет framework в качестве цели, а не просто связывает framework с приложением.
    • Существует способ включить локальный CocoaPods в проект, поэтому не беспокойтесь, что вам не нужно развертывать ваш framework для публики только для этого.

EDIT. Тайлер Лонг работает с Carthage. Проверьте его ответ.

Ответ 4

UICircularProgressRing разрешает это, добавляя файл Swift, содержащий класс @IBDesignable в фреймворке Headers. Чтобы сделать это, выберите проект Xcode фреймворка, выберите цель рамки и перейдите на вкладку Сборка фаз и разверните фазу Заголовки и отпустите файл Swift в Public

Снимок экрана закладки Xcode Build Phases, показывающий фазу заголовков