So what I ended up doing was adding 21 sprites to the game object with an atlas of 12 images each, then depending on which image I want to show, I play the animation from whichever sprite has the image, and make all other sprites invisible.
Here is an example (everything is hard-coded at the moment):
function on_message(self, message_id, message, sender)
if message_id == hash("set_sprite") then
for id = 1, 21 do
go.set("#sprite" .. id, "tint", vmath.vector4())
end
local sprite_id = math.ceil(message.id / 12)
msg.post("#sprite" .. sprite_id, "play_animation", {id = hash("sk" .. message.id)})
go.set("#sprite" .. sprite_id, "tint", vmath.vector4(1))
go.set("#script", "sprite_id", message.id)
end
end
The only issue I am having with this (besides the obvious waste of resources) is that it now crashes on my 1st gen iPad Mini when loading the collection containing all the sprites. It works fine on my Mac and iPhone 6s Plus.
I will look into the other solutions in more depth, but they will require quite a bit a restructuring.