Подтвердить что ты не робот

Использовать общий класс как пользовательский вид в Interface Builder

У меня есть пользовательский подкласс UIButton:

import UIKit

@IBDesignable
class MyButton: UIButton {

    var array : [String]?

}

Это IBDesignable, и я установил его как пользовательский класс для одной из кнопок в моей раскадровке. Я хотел бы сделать его общим, чтобы массив не был одним из объектов String. Итак, я пробовал это:

import UIKit

@IBDesignable
class MyButton<T>: UIButton {

    var array : [T]?

}

Однако я не уверен, как установить это как класс теперь в IB. Я попробовал поставить MyButton<String> или MyButton<Int>, но Interface Builder просто удаляет часть угловых скобок и получает следующую ошибку компиляции:

Command /Applications/Xcode6-Beta4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 254

Есть ли способ использовать общий пользовательский класс, или он не поддерживается?

4b9b3361

Ответ 1

Интерфейс Builder "ведет переговоры" с вашим кодом через среду выполнения ObjC. Таким образом, IB может иметь доступ только к функциям вашего кода, которые могут быть представлены в среде выполнения ObjC. ObjC не делает дженериков, поэтому нет возможности для IB рассказать о времени выполнения, которое специализируется на вашем универсальном классе. (И класс Generic Swift не может быть создан без специализации, поэтому вы получаете ошибку, пытающуюся создать экземпляр MyButton вместо MyButton<WhateverConcreteType>.)

(Вы можете распознать время выполнения ObjC при работе в IB, когда вы нарушаете другие вещи. Попытка использовать чистый класс Swift с выводами/действиями в nib/раскадровке дает ошибки времени выполнения в интроспекции ObjC. Оставляя подключенную розетку, соответствующий код которой декларация изменилась или ушла, дает ошибки времени выполнения в отношении KVC. Et cetera.)

Чтобы игнорировать проблемы во время выполнения и поместить его несколько иначе... вернемся к тому, что должен знать IB. Помните, что система загрузки nib - это то, что создает экземпляр всего раскадровки во время выполнения. Поэтому, даже если части вашего класса, которые принимают параметр типового типа, не являются @IBInspectable, nib все равно должен знать, какую специализацию вашего универсального класса загружать. Таким образом, для того, чтобы IB позволял вам использовать общие классы для представлений, у IB должно быть место для вас, чтобы определить, какую специализацию вашего класса должен использовать nib. Это не так, но это сделает отличный запрос функции.

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

Ответ 2

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

import UIKit

protocol MyDataObjectProtocol{
   var name:String { get}
}

class MyObjectTypeOne:MyDataObjectProtocol{
    var name:String { get { return "object1"}}
}

class MyObjectTypeTwo:MyDataObjectProtocol{
    var name:String { get { return "object2"}}
}

class SpecializedTableViewControllerOne: GenericTableViewController<MyObjectTypeOne> {
}

class SpecializedTableViewControllerTwo: GenericTableViewController<MyObjectTypeTwo> {
}

class GenericTableViewController<T where T:MyDataObjectProtocol>: UITableViewController {
    // some common code I don't want to be duplicated in 
    // SpecializedTableViewControllerOne and SpecializedTableViewControllerTwo
    // and that uses object of type MyDataObjectProtocol.
}

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

import UIKit

protocol MyDataObjectProtocol{
    var name:String { get}
}

class MyObjectTypeOne:MyDataObjectProtocol{
    var name:String { get { return "object1"}}
}

class MyObjectTypeTwo:MyDataObjectProtocol{
    var name:String { get { return "object2"}}
}

class SpecializedTableViewControllerOne: GenericTableViewController {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.object = MyObjectTypeOne()
    }
}

class SpecializedTableViewControllerTwo: GenericTableViewController {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.object = MyObjectTypeTwo()
    }
}

class GenericTableViewController : UIViewController {
    var object : MyDataObjectProtocol?

    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        label.text = object?.name
    }


    // some common code I don't want to be duplicated in 
    // SpecializedTableViewControllerOne and SpecializedTableViewControllerTwo
    // and that uses object of type MyDataObjectProtocol.
}

Теперь, когда я ссылаюсь на SpecializedTableViewControllerTwo

моя выводная метка отображает "object1", когда я связываю SpecializedTableViewControllerOne как пользовательский класс моего контроллера представления в раскадровке и "object2",