Returning a conditional function quickly

I would like to know how to handle conditional returns in swift. For example, I'm returning a custom UICollectionViewCell depending on which collective view delegate is called:

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
      if (collectionView.isEqual(collectionView1)) {
         var cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
         return cell
      }
      else if (collectionView.isEqual(collectionView2)) {
        var cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
        return cell
      }
}

      

The compiler says "There is no return statement in the expeted function to return the UICollectionViewCell", even in both cases I am returning the cell.

I decided to add this

return UICollectionViewCell()

      

at the bottom of the function, but I don't think this is the right way.

I know I can declare the cell above the first "if", change it and return it at the end of the function outside the "if", but then the call to "dequeueReusableCellWithIdentifier" hangs.

Thanks everyone.

+3


source to share


5 answers


To explain @ MidhunMP's answer, right now your code might end up without any return value. For example, take a look at this code, which is similar to yours:

func myFunc() -> Int {
    let myNumber = random() % 3
    if myNumber == 0 {
        return 0
    }
    else if myNumber == 1 {
        return 1
    }
}

      

What if myNumber

equals 2? The function ends with no return value, and it can't be.

Move the return statement to the end of the code, or add a clause else

. Both will ensure that your function will return a value under all circumstances.

You will need:



func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    var cell = UICollectionViewCell()
    if (collectionView.isEqual(collectionView1)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
    } else if (collectionView.isEqual(collectionView2)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
    }
    return cell
}

      

or,

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    var cell = UICollectionViewCell()
    if (collectionView.isEqual(collectionView1)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
    return cell
    } else if (collectionView.isEqual(collectionView2)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
    return cell
    } else {
        return cell;
    }
}

      

However, use the former because it is more graceful and easier to understand.

+4


source


The compiler cannot know what collectionView

will always be collectionView1

or will be collectionView2

in your program and hence gives an error message.

What you can do is add a case else

to make the compiler happy. If all goes well, the case else

will never be fulfilled. If there is a logic error in your program and both conditions if

do not match, then (in "Debug") the program is aborted with an error message.

  if (collectionView.isEqual(collectionView1)) {
       let cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
       // set cell properties ... 
       return cell
  }
  else if (collectionView.isEqual(collectionView2)) {
      let cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
      // set cell properties ... 
      return cell
  }
  else {
      assertionFailure("unexpected collectionView")
      return UICollectionViewCell()
  }

      



Alternatively (and this is just a small variation on the previous two answers), declare cell

as an implicitly expanded option outside of blocks if

:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    var cell : UICollectionViewCell!
    if (collectionView.isEqual(collectionView1)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
        // set cell properties ... 
    } else if (collectionView.isEqual(collectionView2)){
        cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
        // set cell properties ... 
    }
    return cell
}

      

If none of the conditions match, it is returned nil

, which also gives a runtime exception.

+3


source


First of all, everyone in charge, don't use var

. Second, of course, this is the correct compiler of errors, since the delegate method does not guarantee that it collectionView

is either one of yours defined, and requires you to return a valid cell, so if you want to keep the code as cases, then you need to define a valid one. but never used case. Also note that casting your cells to the appropriate subclass is useless here as they are still instantiated as the correct class and still return as UICollectionViewCell

the delegate method signatures suggest.

Here's a faster way to do it:

/// define this in any .swift file included in your project
func ~=<T: AnyObject>(lhs: T, rhs: T) -> Bool {
    return lhs === rhs
}

      

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let reuseIdentifier = cellReuseIdentifierForCollectionView(collectionView)
    return epgCollectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
}

private func cellReuseIdentifierForCollectionView(collectionView: UICollectionView) -> String {
    switch collectionView {
    case collectionView1:
        return "Cell1"
    case collectionView2:
        return "Cell2"
    default:
        return "" // this never happens but is still a bit of a code smell
    }
}

      

+2


source


Since you need to return UICollectionViewCell

, it would be better if you create one var for it and return it (I'm not a fan of writing multiple return statements in a method). Thus, you can change it to something like:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
      var cell = UICollectionViewCell()
      if (collectionView.isEqual(collectionView1))
      {
         cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
      }
      else if (collectionView.isEqual(collectionView2))
      {
         cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
      }
      return cell
}

      

0


source


I'll figure out a solution! This is easier than it sounds. You just need to replace "else if" with "else":

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
  if (collectionView.isEqual(collectionView1)) {
     var cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell1", forIndexPath: indexPath) as Cell1
     return cell
  }
  else (collectionView.isEqual(collectionView2)) {
    var cell = self.epgCollectionView.dequeueReusableCellWithReuseIdentifier("Cell2", forIndexPath: indexPath) as Cell2
    return cell
  }

      

}

Now it works. Thanks everyone guys!

0


source







All Articles