This question already has answers here:
Is it possible to make an Array extension in Swift that is restricted to one class?
(4 answers)
Closed 7 years ago.
Being fairly new to Swift I decided I would look at extending Array (or more specifically [SKTexture] Arrays of SKTexture) with a function to add a specified number of frames from the application bundle.
// FRAMES
FuzzyRabbit_0001#2x.png
FuzzyRabbit_0002#2x.png
FuzzyRabbit_0003#2x.png
FuzzyRabbit_0004#2x.png
// CALL
var rabbitTextures = [SKTexture]()
self.rabbitTextures.textureFromFrames("FuzzyRabbit", count: 4)
My first attempt is listed below, I am getting the error Cannot invoke 'append' with an argument list of type '(SKTexture!)' which from looking at the function fuzzyPush is because I am trying to append an SKTexture rather than the generic T.
Is this possible, or am I limited by the fact that I don't want the function to be generic but rather specific to Arrays of SKTexture.
extension Array {
// ONLY SKTexture
mutating func textureFromFrames(imageName: String, count: Int) {
if !(self[0] is SKTexture) { return }
for index in 1...count {
let image = String(format: "\(imageName)_%04d", index)
let texture = SKTexture(imageNamed: image)
self.append(texture) // ERROR: Cannot invoke append with an argument list of type SKTexture!
}
}
// WORKS FINE
mutating func fuzzyPush(newItem: T) {
self.append(newItem)
}
}
I was just curious if this is something I could do with an extension, its not a problem as I have this as a function that takes 3 parameters (imageName, count, arrayToAppend) so I can quite easily use that.
This extension is not possible to write today. You cannot apply an extension method to only certain types of arrays.
There are two good solutions. You can use a HAS-A pattern by creating a struct (TextureList) that contains a [SKTexture], or you can use a function.
You can replace :
self.append(texture)
with
self.append(texture as T)
I checked this on an array of strings though and it worked.
About the first check add another check to see if the array is empty otherwise the self[0] is SKTexture will fail.
This is the code I tested on an online swift compiler (SKTexture was not available obviously) :
extension Array {
mutating func textureFromFrames(imageName: String, count: Int) {
for index in 1...count {
let image = String(format: "\(imageName)_%04d", index)
self.append(image as T)
}
}
}
var arr = Array<String>()
arr.textureFromFrames("testing", count:4)
for tmp in arr {
println("\(tmp)")
}
Related
How do I keep the reference to an array after an items is appended?
Updated code example, because the prior example didn't seem to be clear enough.
class ViewController: UIViewController {
var numbers = Numbers.singleton.numbers
override func viewDidLoad() {
print(numbers.count)
Numbers.singleton.add(1)
print(numbers.count) // prints 0
print(Numbers.singleton.numbers.count) // prints 1
}
}
class Numbers {
static let singleton = Numbers()
var numbers: [Int]!
private init() {
numbers = []
}
func add(number: Int) {
numbers.append(number)
}
}
Arrays in Swift don't have "references". They are structs, and a struct is a value type. Your (badly named) arrayRef is a separate copy, not a reference to self.array.
Moreover, there is no good reason to want to do what you (seem to) want to do. To have two simultaneous references to a mutable array would be unsafe, since the array can be changed behind your back. The Swift design is sensible; use it, don't subvert it.
I want to remove element of custom type value from an array.
I want to pass a variant instance to function to remove it from array, I don't want to use removeAtIndex().
var favoriteVariants: [Variant]
func removeVariant(variant: Variant)
{
}
If Variant is Equatable and you only want to remove the first one that matches:
if let idx = favoriteVariants.indexOf(variant) {
favoriteVariants.removeAtIndex(idx)
}
If it isn’t Equatable and you have some other matching criteria to find just one to remove:
let idx = favoriteVariants.indexOf {
// match $0 to variant
}
if let idx = idx {
favoriteVariants.removeAtIndex(idx)
}
(these are assuming Swift 2.0 – if 1.2, it’s find(favoriteVariants, variant) instead of indexOf, and there isn’t a version that takes a closure, though it’s not too hard to write one)
If there are multiple ones you want to remove in one go:
favoriteVariants = favoriteVariants.filter {
// criteria to _keep_ any given favorite
}
All of these could be wrapped in extensions if what you want to do is general enough to justify it.
func callFunctionName(parameters: String) -> returnType
{
var somevalue = parameters
var returnValue = somevalue()
return returnValue
}
Is there a way to take in a input and use it as a function name?
example: let say input is green, I want to call function green. if input is red call function red etc...
Or to have a huge if statement to check each input to call different functions
This is not possible in Swift. You will have to store any functions you want to call in your own dictionary, and then use that to look up functions by name.
A "huge statement" might be feasible for a small number of functions, and it would certainly perform faster, but the ideal approach would be to store them in a dictionary.
However, if you are dealing with objects:
if exampleObject.respondsToSelector("exampleFunction")
{
exampleObject.performSelector("exampleFunction")
}
This approach currently works with all classes, be it Objective-C or Swift.
This is easily possible in Objective-C by using:
[self performSelector:NSSelectorFromString(#"green")];
But Swift is less dynamically typed than Objective-C and has less support for reflection. The Objective-C way I described above is very prone to crashes at runtime if the input (e.g. "purple" if you didn't have a function for purple) doesn't match a function that exists.
Using a big if statement is not an unreasonable way to approach it.
As the other answers said, an if statement is probably the best way to go about this.
override func viewDidLoad() {
if someValue = green {
green() //This will run whatever you have in the green() function below
}
}
func green() {
//put code for output of green here
}
Then all you have to do is make separate functions for all your outputs such as the green() func
This is the closest I could get. (And it's partially based on the answer of Vatsal Manot)
The idea is to use closures.
First of all we define the return type of the closure: let's use Int (of course you can change this later).
typealias colorClosureReturnType = Int
Now lets define the type of a closure that receives no parameters (you can change this too) and returns colorClosureReturnType
typealias colorClosureType = () -> (colorClosureReturnType)
Fine, now lets create a dictionary where the key is a String and the value is a closure of type colorClosureType:
let dict : [String: colorClosureType] = [
"red": { return 0 /* you can write here all the logic you need */ },
"green": { return 1 /* also here */},
"blue": { return 2 /* and here */}
]
Usually I let Swift Type Inference to infer the type of the variable/constant. But this time for sake of clarity I explicitly declared the type of the dictionary.
Now we can build a simple function that receives a String and return an optional colorClosureReturnType.
func callClosure(colorName: String) -> colorClosureReturnType? {
return dict[colorName]?()
}
As you can see the function look in the dictionary a closure associated to the key received as param. If it does found it then runs the closure and returns the results.
If the dictionary does not contain the requested key then the function returns nil. That's why the return type of this function is colorClosureReturnType? and not colorClosureReturnType.
Finally some tests:
callClosure("red") // 0
callClosure("green") // 1
callClosure("blue") // 2
func callFunctionName(parameters: String) -> ()
{
_ = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector(parameters), userInfo: nil, repeats: false)
}
func green() {
}
I would like to create a method like this for my projects:
func print(obj: AnyObject) {
if let rect = obj as? CGRect {
println(NSStringFromCGRect(rect))
}
else if let size = obj as? CGSize {
println(NSStringFromCGSize(size))
}
//...
}
But I can't because CGRect and CGSize are structs and do not conform to the AnyObject protocol. So, any ideas on how this could be done?
Use Any instead of AnyObject.
Swift provides two special type aliases for working with non-specific
types:
• AnyObject can represent an instance of any class type.
• Any can represent an instance of any type at all, including function types.
The Swift Programming Language
#nkukushkin's answer is correct, however, if what you want is a function that behaves differently depending on whether it’s passed a CGRect or a CGStruct, you are better off with overloading:
func print(rect: CGRect) {
println(NSStringFromCGRect(rect))
}
func print(size: CGSize) {
println(NSStringFromCGSize(size))
}
In comparison, the Any will be both inefficient (converting your structs to Any and back, could have a big impact if you do this a lot in a tight loop), and non-typesafe (you can pass anything into that function, and it will only fail at runtime).
If your intention is to coerce both types into a common type and then do the same operation on it, you can create a 3rd overload that takes that type, and have the other two call it.
Just discovered a much better method of doing this. Swift has a method called dump, and it works with a lot of kinds of data.
For example:
dump(CGRectMake(0, 5, 30, 60))
Will print:
{x 0 y 5 w 30 h 60}
If you just need to print a CGRect or CGSize, you could use:
println(rect)
or
println(size)
You left a '...' at the end of your function so I assume there are more types that you need to print. To do that you need to make those types conform to the Printable protocol (unless they already do). Here's an example of how -
class Car {
var mileage = 0
}
extension Car : Printable {
var description: String {
return "A car that has travelled \(mileage) miles."
}
}
The you can use:
let myCar = Car()
println(myCar)
Also, you may want to change the format of the way a type is currently printed. For example, if you wanted println(aRect) in the same format as returned by NSStringFromCGRect you could use the extension:
extension CGRect : Printable {
public var description: String {
return "{\(origin.x), \(origin.y)}, {\(size.width), \(size.height)}"
}
}
I have an issue with converting character type to String type. First of all, I have below extension of String for finding nth character within String.
extension String {
func characterAtIndex(index: Int) -> Character? {
var cur = 0
for char in self {
if cur == index {
return char
}
cur++
}
return nil
}
}
I get what I want with this class extension. However when I use that nth character for title of my custom UIButton, gives an error. My Uibutton Class is
class hareketliHarfler: UIButton {
init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}
func getLetter(letter:String!){
self.titleLabel.text = letter
}
}
The error show when i try to access "getLetter(letter:String)" function. Here is example of main view Controller codes:
var harfim = hareketliHarfler(frame: CGRectMake(100,100,100,100))
var str="This is my String"
var bufi=str.characterAtIndex(3)
harfim.getLetter(bufi as AnyObject) ****
In * section I try .getLetter(bufi), .getLetter(bufi as String) also I try to change parameter type of function. Look like: func getLetter(letter:Character!) or func getLetter(letter:AnyObject!)...etc
Didn't find a way. Need a help on that. Thank you
How about the simple
String(theCharacter)
Works in Swift 4 and Swift 5
Your problem is quite simple: your characterAtIndex function returns a Character, and self.titleLabel.text is a String. You can't convert between the two implicitly. The easiest way would be to turn the Character into a String using the String initialiser:
// ch will be Character? type.
if let ch = str.characterAtIndex(3) {
// Initialise a new String containing the single character 'ch'
harfim.getLetter(String(ch))
} else {
// str didn't have a third character.
}
Unlike other solutions, this is safe for unusual Unicode characters, and won't initialise a potentially large array or iterate the whole String just to get the third character.
Change this:
var bufi=str.characterAtIndex(3)
harfim.getLetter(bufi as AnyObject)
to this:
harfim.getLetter(String(Array(str)[3]))
So what happening here:
we create an array from our string. Array elements are symbols from original string. Such break down correctly tracks symbols that are presented with a sequences of two or more code points. E.g. emoji or flag as noted by #MartinR.
We access element at 4-th position.
Note that as we crate an array from initial string then performance wise is better to use this method only with short strings and avoid it in oft-repeated routines. But in your case it seems to be OK.
Can also use Character(text).isNumber if you want to get localised numbers.
Reference:
https://developer.apple.com/documentation/swift/character/3127015-isnumber