#ios #swift #variadic-functions #nsexpression
#iOS #swift #variadic-функции #nsexpression
Вопрос:
У меня есть немного интересный вопрос, и я не уверен, возможно ли то, что я хочу сделать. Если нет, я полагаю, что есть какой-то другой обходной путь для моей проблемы.
Я использую Mapbox (альтернативу MapKit) для iOS. В Mapbox есть слои стилей, которые позволяют устанавливать разные изображения для разных контактов на карте. Это делается путем установки для слоя iconImageName
другого значения в зависимости от pin-кода карты, с которым он имеет дело. Значение, которое задается в качестве уровня стиля, iconImageName
является NSExpression
значением, в котором мы передаем формат в виде строки и любого другого числа NSExpression
, чтобы они соответствовали формату. Ниже приведен пример создания слоя стиля из изображений, которые не являются динамическими, т. Е. Эти изображения жестко запрограммированы в приложении.
//Create the layer
let ports = MGLSymbolStyleLayer(identifier: "ports", source: source)
//Create a default shape
let defaultShape = NSExpression(forConstantValue: "grayMarker")
//For every pin in the 'ports' layer, we are going to look at its
//'type' property. If 'type' is equal to the string 'redPin', its image
//should be the one registered with the name 'redMarker'. If 'type' is
//equal to the string 'bluePin', its image should be the one registered
//with the name 'blueMarker'...and so on. If 'type' doesn't match
//anything outlined here, then set the pin's image to the default
//shape.
ports.iconImageName = NSExpression(format: "MGL_MATCH(type, 'redPin', %@, 'bluePin', %@, 'greenPin', %@, 'grayPin', %@, %@)", NSExpression(forConstantValue: "redMarker"), NSExpression(forConstantValue: "blueMarker"), NSExpression(forConstantValue: "greenMarker"), NSExpression(forConstantValue: "grayMarker"), defaultShape)
//Add the layer to the map's style
self.mapStyle!.addLayer(ports)
Итак, это очень легко сделать, когда информация жестко запрограммирована в приложении. Проблема в том, что у меня есть ряд «типов выводов», которые возвращаются из запроса API, которые мне, возможно, потребуется изменить в будущем. С этими типами выводов связан photoUrl.
Поскольку количество типов выводов может измениться в будущем, я должен в своем приложении извлекать типы, анализировать их в массив пользовательских объектов, перебирать каждый из них и добавлять их информацию в формат NSExpression . Ниже приведен пример того, как я это делаю:
//Create a new style layer, like in the previous example
let icons = MGLSymbolStyleLayer(identifier: "icons", source: source)
//Create a prefix type string for our format string. We will modify
//this based on the data in our categories
var format = "MGL_MATCH(type, "
//Loop through the array and modify our format string to include our
//pin type
for pinType in self.pinTypes {
format = "'(pinType.id ?? "")', %@, "
}
//Add the suffix to the format string
format = "%@)"
Конечный результат нашей строки ‘format’ всегда будет выглядеть примерно так (хотя и с разным количеством точечных типов):
MGL_MATCH(type, '0lah2lqnit8sae8', %@, 'moyxexbimf988g3', %@, 'rweisxrjjahriou', %@, 'phn9kssirq6p99f', %@, 'wgpyy6bvw0dxmfp', %@, 'knj2q61ip0xfspy', %@, 'q8e5zqkm8aj9bvl', %@, '16rxmcilhes742c', %@, 'c2srv0rkx3wtagm', %@, 'jorbix53907eikq', %@, 'L1E5cRH2mVWC5qp', %@, 'Aur0Ok57zrtxBiL', %@, 'i5261q9qjqaftfh', %@, '9ru1hhcjwqx4c51', %@, 'ebnxme3pwq6q7o3', %@, 'oyn45ntbub7upei', %@, 'hb1fy24bme5e040', %@, 'xr2pmgtged1w678', %@, '97st6t0fwb6anwz', %@, 'ovwe99ejboz7zpb', %@, 'amvm4xe85s0g6sx', %@, 'gj801lf4co3h1zm', %@, '7emo3defagedy6l', %@, '9atby0ig427fkc4', %@, '6w4asp3yxs4e6ez', %@, 'tdmmyqwtn5ncy55', %@, 'yd4epiob1mg6tc3', %@, 'icb59kmni3thlmd', %@, 'eh9mgf4lp50ar88', %@, 'lxjccng4fb7sk05', %@, '2jg3aqkltbsktof', %@, 'e0otypxpbq2syzm', %@, '25af3o1wxo77s8b', %@, '1r6z9zdi8uxtf7m', %@, 't1zxv955vw5dfep', %@, 'iq93veeuccsrqye', %@, 'osviabneknsqo2x', %@, 'u6mps2zv2ivs4n8', %@, 'r2q9u8dhhk94km4', %@, 'wp6jmyyeh17nocd', %@, '4now4xnrylx7010', %@, 'f8uy2twfr2r3m7f', %@, 'lhw9bs31nr2twlx', %@, 'qvnfna00n9wnkgu', %@, 'g8f5zc7gcei1aax', %@, 'spxlscffbf0ve9q', %@, 'dir96qh9w1n43ys', %@, 'dgrj9voh1ybhv5j', %@, 'hdwp8w1lfhcurq9', %@, 'twpx9aeb5kkkju2', %@, 'eb85y3w2zywfpet', %@, '9dd6yp6c3e4oyno', %@, 'd61qrfm60vq4mqh', %@, 'hcjxvgzr0kiqbsf', %@, 'izygh92tmdd5r07', %@, 'ymd2p3k5voo27te', %@, 'l87mls4z0zy534u', %@, 'ybr1twmjafdr1cf', %@, 'eqeio0phb1gj50y', %@, 'dn48bxo5hkt7295', %@, 'uE5z1pDR6U1pPhR', %@, '6801ek42qsn1hl2', %@, 'BWPcCGJ0bTlqYhj', %@, 'wzEaDVI28xvuENW', %@, 'yiXttIPk0oLQAc3', %@, 'b6nadw9emiband2', %@, 'yxt8w275plqxws4', %@, '99lo09p6wr8wcdv', %@, '1hhoeiony8jt0rx', %@, 'bkcmo89dcvdh7px', %@, 'nz8d748p4np9bll', %@, '6vpts6ytusz51n5', %@, %@)
Итак, я могу получить строку формата, созданную с использованием динамических данных, извлеченных из нашего запроса API, но теперь начинается сложная часть. Как мне передать динамическое количество аргументов в функцию?!
Обратите внимание, что следующим шагом будет присвоение iconImageName
свойству значения NSExpression, которое может принимать любое количество аргументов. Вспомните наш первый пример с жестко закодированными данными:
ports.iconImageName = NSExpression(format: "MGL_MATCH(type, 'redPin', %@, 'bluePin', %@, 'greenPin', %@, 'grayPin', %@, %@)", NSExpression(forConstantValue: "redMarker"), NSExpression(forConstantValue: "blueMarker"), NSExpression(forConstantValue: "greenMarker"), NSExpression(forConstantValue: "grayMarker"), defaultShape)
Первый аргумент — это строка формата, все остальные аргументы — это NSExpression
s, которые «заполняют» эту строку формата. Как я могу «выполнить цикл» по массиву pinTypes и создать отдельный NSExpression
для каждого из них и передать каждый отдельный NSExpression
в качестве аргумента при настройке свойства iconImageProperty слоя стиля?
TLDR Есть ли способ, которым вы можете, передавая аргументы в функцию, которая может принимать любое количество аргументов, перебирать массив потенциально изменяющегося размера, создавать объект из каждого элемента в этом массиве, а затем передавать его в качестве аргумента в функцию, которая может принимать любое количество аргументов?
Ответ №1:
Возможно, я неправильно понимаю вопрос. Я не знаю, нужен ли вам общий способ сделать это, но NSExpression
есть инициализация, которая принимает массив:https://developer.apple.com/documentation/foundation/nsexpression/1413484-init
Итак, вы могли бы сделать что-то вроде:
let defaultShapeExpression = NSExpression(forConstantValue: "grayMarker")
// Either do clean array or add your initial arguments
var expressionArguments = [Any]()
var format = "MGL_MATCH(type, "
for pinType in self.pinTypes {
format = "'(pinType.id ?? "")', %@, "
expressionArguments.append(NSExpression(pinType.iconImageProperty))
}
format = "%@)"
expressionArguments.append(defaultShapeExpression)
let expression = NSExpression(format: format, argumentArray: expressionArguments)
Надеюсь, это поможет. Я сижу за своим рабочим столом, поэтому я ничего не могу проверить сам.
Комментарии:
1. Огромное спасибо, я никогда раньше не использовал NSExpressions, но понимаю, что они являются аналогами NSPredicate. Я думал, что мог бы передать массив в качестве аргумента, но еще не выяснил, есть ли у NSExpression инициализация для приема массивов. В примере Mapbox инициализация принимала строку формата, а затем переменные аргументы. Позвольте мне попробовать это и вернуться к вам!
2. На самом деле это сработало отлично. Мне пришлось немного отредактировать NSExpressions, поскольку мое понимание того, как они работают, было немного неточным, но пока массив NSExpressions правильный, передача массива в качестве аргумента отлично работает.