I have a bit of an interesting question, and am not sure if what I am wanting to do is possible. If not, I imagine there is some other workaround for my issue.
I am using Mapbox (MapKit alternative) for iOS. Mapbox has style layers which let you set different images for different pins on a map. This is done by setting the layer's iconImageName
to a different value depending on the map pin it's dealing with. The value that is given as the style layer's iconImageName
is an NSExpression
, in which we pass a format as a string and any other number of NSExpression
so that they fit the format. Below is an example of creating a style layer from images that are not dynamic, i.e. these images are hard-coded into the app.
//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)
So, this is very easy to do when the information is hard-coded into the app. The problem is, I have a series of "pin types" that are returned from an API request, that I may need to change down the road. These pin types have a photoUrl associated with them.
As the number of pin types may change in the future, I have to, within my app, pull the types, parse them into an array of custom objects, and loop through each one and add their info into the NSExpression's format. Below is an example of how I am doing that:
//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 += "%@)"
The end result of our 'format' string will always look something like this (though with a varying number of pinTypes):
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', %@, %@)
So, I am able to get the format string created with dynamic data pulled from our API request, but now comes the hard part. How do I pass a dynamic number of arguments into a function?!
Note that the next step is to set the iconImageName
property to an NSExpression that can take any number of arguments. Recall our first example with hard-coded data:
ports.iconImageName = NSExpression(format: "MGL_MATCH(type, 'redPin', %@, 'bluePin', %@, 'greenPin', %@, 'grayPin', %@, %@)", NSExpression(forConstantValue: "redMarker"), NSExpression(forConstantValue: "blueMarker"), NSExpression(forConstantValue: "greenMarker"), NSExpression(forConstantValue: "grayMarker"), defaultShape)
The first argument is the format string, all other arguments are NSExpression
s that are 'populating' that format string. How can I 'loop' through the pinTypes array and create an individual NSExpression
for each one and pass each individual NSExpression
as an argument when setting the style layer's iconImageProperty?
TLDR Is there a way you can, when passing arguments into a function that can take any number of arguments, loop through an array of potentially varying size, create an object from each element in that array, and then pass it as an argument into the function that can take any number of arguments?
I may be misunderstanding the question. I don't know if you want a general way to do this, but NSExpression
has an init that takes an array: https://developer.apple.com/documentation/foundation/nsexpression/1413484-init
So you could do something like:
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)
Hopefully this helps. I'm sitting at my desktop, so I can't really test anything myself.