How do I check the number of values in an array th

2019-08-11 00:29发布

问题:

I am new to Swift (I learned it about a month ago) and I'm creating a custom UICollectionViewCell class, and I need three types of UIButtons and other objects as well, but I don't want to write each one out, so I am creating a function. However, since I have three buttons, the function must take exactly three arrays for each parameter. Can someone please help me with the code where I have included the comments? Thank you!

var buttonArray: [UIButton] = []
var checkBoxArray: [UIView] = []
var labelArray: [UILabel] = []

func initializeStuff(objectType: AnyObject, xOriginCgFloat: [CGFloat], yOriginCgFloat: [CGFloat], width: [CGFloat], height: [CGFloat])  {

    if xOriginCgFloat != Array() || yOriginCgFloat != Array() || width != Array() || height != Array() {
        fatalError("Each CgFloat parameter must be an array.")
    } else {
        if AnyObject.self == UIButton.self {
            if condition { //condition: each array must contain exactly three values
                for index in 0..<3 {
                    var button = UIButton(frame: CGRect.init(origin: CGPoint.init(x: xOriginCgFloat[index], y: yOriginCgFloat[index]), size: CGSize.init(width: width[index], height: height[index])))

                    buttonArray.append(button)
                }
            } else {
                fatalError("Each array for a button object must have exactly three values.")
            }

        } else if AnyObject.self == UIView.self {
            if condition2 { //condition2: each array must contain exactly two values
                for index in 0..<2 {
                    var checkBox = UIView(frame: CGRect.init(origin: CGPoint.init(x: xOriginCgFloat[index], y: yOriginCgFloat[index]), size: CGSize.init(width: width[index], height: height[index])))

                    checkBoxArray.append(checkBox)
                }
            } else {
                fatalError("Each array for a view object must have exactly two values.")
            }

        } else if AnyObject.self == UILabel.self {
            if condition3 { //condition3: each array must contain exactly two values
                for index in 0..<2 {
                    var label = UILabel(frame: CGRect.init(origin: CGPoint.init(x: xOriginCgFloat[index], y: yOriginCgFloat[index]), size: CGSize.init(width: width[index], height: height[index])))

                    labelArray.append(label)
                }
            } else {
                fatalError("Each array for a view object must have exactly two values.")
            }

        } else {
            fatalError("Incorrect type of object; AnyObject parameter must be a UIButton, UIView, or UILabel.")


        }
    }
}

回答1:

There's a lot going on here. First off, the way you're checking if your parameters are arrays won't work:

if xOriginCgFloat != Array() {

Array() declares a new object of type Array and checks if xOriginCgFloat is equal to it. Instead, you want to check if xOriginCgFloat is of the Array type, which you can do like this:

if xOriginCgFloat is Array {

But! you don't need to do this. Your function declaration specifies xOriginCgFloat: [CGFloat], which means that for the function to run, the value passed for xOriginCgFloat must be an Array of CGFloats. No need to check.

OK next up: when you say

if AnyObject.self == UIButton.self {

you probably think you're checking if the value passed for ObjectType is UIButton. But you're not. That's actually checking if the class AnyObject is the same as the class UIButton, which it isn't. So that will always return false. Instead, you can perform the check you're looking for like this:

if objectType === UIButton.self {

this will succeed if you pass UIButton.self for objectType when you call the function, which I assume is your intention.

Lastly, your actual question: if you want to know how many elements your xOriginCgFloat array has, you can use xOriginCgFloat.count:

if xOriginCgFloat.count == 2 {

This will work, but it's not an ideal approach, as it doesn't support compile-time checking; if somewhere in your program you pass an array with the wrong number of values, the compiler can't catch it. You won't find out until the program is running, and maybe at a very bad time.

Instead, if you know the number of values you're looking for, use a tuple. You can declare a function like this:

func initializeStuff(xOriginCgFloat: (CGFloat, CGFloat)) {

And call it like this:

initializeStuff(xOriginCgFloat: (5.0, 6.7))

It expects a tuple with exactly two CGFloats inside, so it will show an error in the IDE if you try to call with anything else. When your function is called, you don't need to check as you're guaranteed exactly the values you wanted.

The downside of tuples is that you can't really iterate them with a for loop, (actually you can, but it's messy) so you have to do things manually. Since you only have two or three items in your tuple this might not be a bad approach. For example:

let xOriginCgFloat:(CGFloat, CGFloat) = (4, 6)
var floats = [CGFloat]()
floats.append(xOriginCgFloat.0)
floats.append(xOriginCgFloat.1)
for float in floats {
    // process xOriginCgFloat member here
}

You can read up on tuples in Apple's documentation.



回答2:

To write an if that checks the amount of elements in an array simply do:

if array.count == 3

I don't know if I got it right, but you can do something like:

if buttonArray.count == 3{
    //do something
}

hope have helped



回答3:

I'm not sure I agree with your approach to have a func to build all the objects, but I definitely wouldn't take an array of params that has to be a fixed length. You can try something like this:

var buttonArray: [UIButton] = []
var checkBoxArray: [UIView] = []
var labelArray: [UILabel] = []

struct ObjectDefinition {
    let classType: UIView.Type
    let frame: CGRect
}

func createAllTheStuff() {
    let objectDefinitions: [ObjectDefinition] = [
        ObjectDefinition(classType: UIButton.self, frame: CGRect(x: 0, y: 0, width: 100, height: 200)),
        ObjectDefinition(classType: UIButton.self, frame: CGRect(x: 10, y: 10, width: 10, height: 25)),
        ObjectDefinition(classType: UIView.self, frame: CGRect(x: 0, y: 0, width: 50, height: 210)),
        ObjectDefinition(classType: UILabel.self, frame: CGRect(x: 0, y: 0, width: 1200, height: 20))
    ]

    for objectDefinition in objectDefinitions {
        initializeStuff(objectType: objectDefinition.classType, frame: objectDefinition.frame)
    }
}


func initializeStuff(objectType: UIView.self, frame: CGRect)  {
    switch objectType {
    case UIButton.self:
        let button =  UIButton(frame: frame)
        buttonArray.append(button)
    case UILabel.self:
        let label = UILabel(frame: frame)
        labelArray.append(label)
    case UIView.self:
        let checkBox = UIView(frame: frame)
        checkBoxArray.append(checkBox)
    }
}

However, I think that unless you are doing a lot more boilerplate than what you've shown, you could just as easily get away with this. Its actually shorter, since you aren't really don't anything else that needs to be encapsulated. If you are, you should make a factory method to preconfigure the UIButton, UILabel, or UIView

var buttonArray: [UIButton] = []
var checkBoxArray: [UIView] = []
var labelArray: [UILabel] = []


func createAllTheStuff() {
    let button1 = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
    buttonArray.append(button1)
    let button2 = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
    buttonArray.append(button2)
    ... etc ...
    let objectDefinitions: [ObjectDefinition] = [
}