Проголосовал за Догберта :-)
спрашиваю о точном коде, но об общей идее.
Вот моя проблема: я пытаюсь создать нечто похожее на фильтр, выбирающий пользовательский интерфейс в приложении Photos. Я пробовал несколько подходов, и все они имеют свои недостатки.
1) я пробовал использоватьOperation
а такжеOperationQueue
с представлением коллекции, какая предварительная выборка включена. Это быстро загружает viewController, но при прокрутке отбрасывает кадры.
2) Прямо сейчас я использую вид прокрутки иGCD
но он загружает viewController слишком долго (потому что он применяет все фильтры сразу ко всем кнопкам внутри него), но затем он плавно прокручивается.
НОТА: Чтобы ответить на этот вопрос, нет необходимости читать приведенную ниже часть (я полагаю), однако, если вам интересно, как я пытаюсь реализовать функциональность, вы можете прочитать ее.
Для реализации всех фильтров я использую структуру под названиемFilters
который отвечает за запуск каждого фильтра и добавление его в массив.
struct Filters {
var image: UIImage
var allFilters: [CIFilter] = []
init(image: UIImage) {
self.image = image
guard let sepia = Sepia(image: image) else {return}
allFilters.append(contentsOf: [sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia, sepia])
}
}
Прямо сейчас я использую только один фильтр.Sepia
это подклассCIFilter
, Я создал его как подкласс, потому что в будущем я собираюсь создать собственный из него. Вот его реализация:
class Sepia: CIFilter {
var inputImage: CIImage?
var inputIntensity: NSNumber?
@objc override var filterName: String? {
return NSLocalizedString("Sepia", comment: "Name of a Filter")
}
convenience init?(image: UIImage, inputIntensity: NSNumber? = nil) {
self.init()
guard let cgImage = image.cgImage else {
return nil
}
if inputIntensity != nil {
self.inputIntensity = inputIntensity
} else {
self.setDefaults()
}
let inputImage = CIImage(cgImage: cgImage)
self.inputImage = inputImage
}
override func setDefaults() {
inputIntensity = 1.0
}
override var outputImage: CIImage? {
guard let inputImage = inputImage, let inputIntensity = inputIntensity else {
return nil
}
let filter = CIFilter(name: "CISepiaTone", withInputParameters: [kCIInputImageKey: inputImage, kCIInputIntensityKey: inputIntensity])
return filter?.outputImage
}
}
В представлении контроллераviewDidLoad
Я начинаюFilters
структура:
self.filters = Filters(image: image)
Затем я вызываю метод, который настраивает некоторые представления (filterViews
) на основе количества фильтров вfilters.allFilters
массив и перебирает их и вызывает метод, который принимает миниатюруUIImage
и применяет фильтр к нему, а затем возвращает его в обработчике завершения (я используюDispatchGroup
в нем по причинам отладки). Вот метод, который применяет фильтр к миниатюре:
func imageWithFilter(filter: CIFilter, completion: @escaping(UIImage?)->Void) {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
guard let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage, let cgImageResult = self.context.createCGImage(outputImage, from: outputImage.extent) else {
DispatchQueue.main.async {
completion(nil)
}
group.leave()
return
}
let filteredImage = UIImage(cgImage: cgImageResult)
DispatchQueue.main.async {
print (filteredImage)
completion(filteredImage)
}
group.leave()
}
group.notify(queue: .main) {
print ("Filteres are set")
}
}
Заявление о печати выше и адрес отфильтрованного изображения печатаются довольно скоро, однако изображения не отображаются внутри представлений.
Я пытался использоватьTime Profiler
но это дает мне странные результаты. Например, он показывает, что выполнение в корне обратного следования занимает довольно много времени:
Когда я пытаюсь увидеть код в Xcode, я получаю следующее, что мало помогает:
Итак, это проблема. Если у вас есть какие-либо идеи о том, как это реализовано в приложении «Фотографии», что оно настолько быстрое и отзывчивое, или если у вас есть предложения о моей реализации, я был бы очень признателен за вашу помощь.