Как загрузить изображение в prepareForInterfaceBuilder с помощью IBDesignable UIImageView

Я хотел бы загрузить образец изображения в дизайн IB UIImageView, чтобы отображаться в Интерфейсном Разработчике при редактировании интерфейса. Следующий код не работает, так как заполнитель представления в IB остается пустым (область просмотра содержит только текст UIImageView):

@IBDesignable
class TestImageView : UIImageView
{
    override func prepareForInterfaceBuilder() {
        //let bundle = NSBundle.mainBundle()
        let bundle = NSBundle(forClass: nil)
        let imagePath = bundle.pathForResource("Test", ofType: "jpg")
        self.image = UIImage(contentsOfFile: imagePath)
    }
}

Обратите внимание, что:

  • в IB правильный класс Custom для представления корректен (TestImageView)
  • Test.jpg присутствует в проекте (если я вручную установить свойство изображения UIImageView в IB изображение появляется).
  • Я попробовал два разных способа получения пакета в коде

Это было проверено с Xcode 6 beta 3.

Обновление: в обоих случаях путь к пакету, который я получаю, "/Applications/Temporary/Xcode6-Beta3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays", На этом пути изображение явно не присутствует.

7 ответов

Решение

В Swift 4.1 код @kasplat изменился на:

    // DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS
    let designTimeBundle = Bundle(for: type(of: self))

    // OR ALTERNATIVELY BY PROVDING THE CONCRETE NAME OF YOUR DESIGNABLE VIEW CLASS
    let designTimeBundle = Bundle(for: YourDesignableView.self)

    // AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE
    let image = UIImage(named: "Logo", in: designTimeBundle, compatibleWith: nil)

Попробуйте получить связку класса следующим образом:

let bundle = NSBundle(forClass: self.dynamicType)

или указав имя класса, как это

let bundle = NSBundle(forClass: TestImageView.self)

Предполагая, что ваше изображение находится в комплекте, например, Images.xcassets, вы можете загрузить его, используя:

self.image = UIImage("Test", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)

Не забудьте проверить, является ли ваше изображение нулевым, прежде чем пытаться его использовать. Я не смог получить путь к изображению, используя bundle.pathForResource для правильной работы с обычными изображениями. Также, похоже, нет вызова UIImage, в котором вы указываете только имя и пакет, поэтому вы должны использовать коллекцию признаков.

Этот вопрос связан с:

xcode 6 IB_DESIGNABLE - не загружать ресурсы из пакета в Интерфейсном Разработчике

Ответ от Apple...

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

Мы не можем сделать это проще, чем указать комплект. Вы могли бы сказать: "О, давайте пошлём -[NSBundle mainBundle]", но многие сайты вызовов, которые ссылаются на пакет, не проходят через него (или проходят через CF API). Кто-то может сказать тогда: "Хорошо, ну, как насчет того, чтобы мы хотя бы пошутили -[UIImage imageNamed:]". Проблема здесь в том, что нет единой замены для основного комплекта. У вас может быть одновременно загружено несколько пакетов живого просмотра (фреймворки или приложения), поэтому мы не можем просто выбрать один из них как основной.

Разработчики должны знать о пакетах и ​​о том, как получить изображения из пакета. Разработчики должны использовать UIImage(с именем:inBundle:compatibilityWithTraitCollection:) для всех поисков изображений.

Давай заглянем в быстрый ответ 3

let bundle = Bundle(for: self.classForCoder)
... UIImage(named: "AnImageInYourAssetsFolderPerhaps", in: bundle, compatibleWith: self.traitCollection)!

Для Swift 4 (и 3) используйте это:

let image = UIImage(named: "foo", in: Bundle(for: type(of: self)), compatibleWith: traitCollection)

Этот подход получает расслоение универсально

Мне удалось решить проблему с получением пути проекта Interface Builder из текущего NSProcessInfo объект. Затем вы можете выбрать правильный путь из IB_PROJECT_SOURCE_DIRECTORIES ключ.

override func prepareForInterfaceBuilder() {
    let processInfo = NSProcessInfo.processInfo()
    let environment = processInfo.environment
    let projectSourceDirectories : AnyObject = environment["IB_PROJECT_SOURCE_DIRECTORIES"]!
    let directories = projectSourceDirectories.componentsSeparatedByString(":")

    if directories.count != 0 {
        let firstPath = directories[0] as String
        let imagePath = firstPath.stringByAppendingPathComponent("PrepareForIBTest/Test.jpg")

        let image = UIImage(contentsOfFile: imagePath)
        self.image = image
    }
}

Этот метод описан в сеансе WWDC 2014 411 "Что нового в Интерфейсном Разработчике " в соответствии с предложением bjhomer в этом сообщении на форумах разработчиков Apple.

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

Это загрузит ваш IB_Designable, или, если у вас его нет - изображение по умолчанию.

- (void)prepareForInterfaceBuilder
{
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    imagePic = imagePic ? [imagePic imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] : [UIImage imageNamed:@"goodIcon" inBundle:bundle compatibleWithTraitCollection:self.traitCollection];

}

Для Swift 2 и Xcode 7 интерфейс был изменен. Следует использовать

let bundle = NSBundle(forClass: self.dynamicType)
let image = UIImage(named: "imageName", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
let imageView = UIImageView(image: image)

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

Другие вопросы по тегам