Why does Xcode show warning 'Will never be executed' on my 'if' clause? - swift

I want to show a note/label on my tableview background when/if there is no data to load, or data is being fetched etc.
I can't see what am I doing wrong here. Xcode is showing a warning "Will never be executed" on this line of code: if mostUpTodateNewsItemsFromRealm?.count < 1 {
Here is the method.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// create a lable ready to display
let statusLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
statusLabel.textColor = globalTintColor
statusLabel.textAlignment = NSTextAlignment.Center
self.tableView.backgroundView = statusLabel
self.tableView.backgroundView?.backgroundColor = colourOfAllPickerBackgrounds
// 1) Check if we have tried to fetch news
if NSUserDefaults.standardUserDefaults().valueForKey("haveTriedToFetchNewsForThisChurch") as! Bool == false {
statusLabel.text = "Busy loading news..."
} else {
// If have tried to fetch news items = true
// 2) check church has channels
let numberOfChannelsSubscribedToIs = 0
if let upToDateSubsInfo = upToDateChannelAndSubsInfo {
let numberOfChannelsSubscribedToIs = 0
for subInfo in upToDateSubsInfo {
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs + 1
}
}
}
if numberOfChannelsSubscribedToIs < 1 {
// if no channels
// show messsage saying you aren't subscribed to any channels.
statusLabel.text = "Your church hasn't setup any news channels yet."
} else {
// 3) if we have tried to fetch news AND the church DOES have channels
// check if we have any news items to show
if mostUpTodateNewsItemsFromRealm?.count < 1 {
// If no news items
statusLabel.text = "Your church hasn't broadcast and news yet."
} else {
// if have tried to fetch AND church has channels AND there ARE news items
// remove the background image so doesn't show when items load.
self.tableView.backgroundView = nil
}
}
}
// in all circumstances there will be one section
return 1
}

Your code first created a constant:
let numberOfChannelsSubscribedToIs = 0
And then you check whether it is less than 1:
if numberOfChannelsSubscribedToIs < 1
Since it is a constant, it will never change. This means that the if clause will always be executed. Thus, the else clause will never be executed.
So first you need to make this constant variable:
var numberOfChannelsSubscribedToIs = 0
Then just change this:
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs + 1
}
to this:
if subInfo.subscribed == true {
numberOfChannelsSubscribedToIs += 1
}
This way, numberOFChannelSubscribedToIs can be some number other than 0. And the else clause can be executed.
var and let are very different!

Related

Is it possible to somehow track the state when a scroll is needed in a UITextView? Swift

I working in collectionViewCell. I upload API data, and I want to check if the number of lines in the UITextView is more than 4, then I need a isScrollEnabled = true, if not, then = false. How to implement this?
You can try something like this to get number of lines
extension UITextView {
func numberOfLines() -> Int {
let layoutManager = self.layoutManager
let numberOfGlyphs = layoutManager.numberOfGlyphs
var lineRange: NSRange = NSMakeRange(0, 1)
var index = 0
var numberOfLines = 0
while index < numberOfGlyphs {
layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
index = NSMaxRange(lineRange)
numberOfLines += 1
}
return numberOfLines
}
}
To call it
let count = textView.numberOfLines()
print(count)

Swift+UIImageView: switch between two animation frame sequences?

Example of code for animation:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
NOTE: Both startAnimation calls are in the main thread but not in the same run loop.
In my case I want to change jumpImages to another set of images. I should cancel the previous animation and start the new one but it looks like I set the last image in jumpImages sequence instead.
How to solve this issue?
Okay, I think I'm understanding your problem now -- from your statement, "[...] but it looks like I set the last image in jumpImages sequence instead," I think you mean that when you call startAnimation(index: 1), you are seeing just the last frame of the index 0 animations, and none of the index 1 animations.
Assuming that is right, your problem here going to be a race condition.
When you call startAnimation() then second time with index 1, the first animation may or may not still be in progress.
The solution will be to call self.imageView.stopAnimating() before you change all the images and start the new animation. The best practice would be to check the imageView.isAnimating flag before you call it. Something like this:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if self.imageView.isAnimating {
self.imageView.stopAnimating()
}
self.imageView.frame.origin.y = self.imageView.frame.origin.y + 100.0
self.imageView.animationImages = images
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 1
self.imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)
Also, since you're in a function and not within a closure, you can remove all those references to self., which makes things a bit shorter:
func startAnimation(index: Int) {
var jumpImages = ["Jump_1","Jump_2","Jump_3","Jump_4","Jump_5","Jump_6","Jump_7","Jump_8","Jump_9","Jump_10"]
if index == 1 {
jumpImages = ["Jump_11","Jump_21","Jump_31","Jump_41","Jump_51","Jump_61","Jump_71","Jump_81","Jump_91","Jump_101"]
}
var images = [UIImage]()
for image in jumpImages{
images.append(UIImage(named: image)!)
}
if imageView.isAnimating {
imageView.stopAnimating()
}
imageView.frame.origin.y = imageView.frame.origin.y + 100.0
imageView.animationImages = images
imageView.animationDuration = 2.0
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
startAnimation(index: 0)
...
startAnimation(index: 1)

Quickest way to check multiple bools and changing image depending on that bool

I have multiple achievements that the user can achieve during the game. When the user goes to the achievement VC, he should see an image when the achievement is accomplished.
I am now using this code in the viewDidLoad function:
if unlockedAchievement1 == true
{
achievement1Text.textColor = UIColor.greenColor()
achievement1Image.image = UIImage(named: "achievement1Unlocked")
}
if unlockedAchievement2 == true
{
achievement2Text.textColor = UIColor.greenColor()
achievement2Image.image = UIImage(named: "achievement2Unlocked")
}
This would work, but it takes a lot of time to copy paste this overtime. How can I shorten this? Should I make a class? I read about for in loops, but I did not quite understood that. Any help is welcome!
I feel like I have seen this before. But anyway...
//use array of bools. First declare as empty array.
//Use Core data to achieve saving the Booleans if you need to.
let achievements:[Bool] = []
let achievementsImage:[UIImage] = []
let achievementsText:[UITextView] = []
func viewDidLoad() {
//Say you have 5 achievements.
for index in 0 ..< 5 {
achievements.append(false)
achievementsImage.append(UIImage(named: "achievementLocked"))
achievementText.append(UITextView())
//Then you can edit it, such as.
achievementText[index].frame = CGRect(0, 0, 100, 100)
achievementText[index].text = "AwesomeSauce"
self.view.addSubview(achievementText[index])
achievementText[index].textColor = UIColor.blackColor()
}
}
//Call this function and it will run through your achievements and determine what needs to be changed
func someFunction() {
for index in 0 ..< achievements.count {
if(achievements[index]) {
achievements[index] = true
achievementsImage[index] = UIImage(named: String(format: "achievement%iUnlocked", index))
achievementsText[index].textColor = UIColor.greenColor
}
}
}
I'd recommend making your unlockedAchievement1, unlockedAchievement2, etc. objects tuples, with the first value being the original Bool, and the second value being the image name. You'd use this code:
// First create your data array
var achievements = [(Bool, String)]()
// Then you can make your achievements.
// Initially, they're all unearned, so the Bool is false.
let achievement1 = (false, "achievement1UnlockedImageName")
achievements.append(achievement1)
let achievement2 = (false, "achievement2UnlockedImageName")
achievements.append(achievement2)
// Now that you have your finished data array, you can use a simple for-loop to iterate.
// Create a new array to hold the images you'll want to show.
var imagesToShow = [String]()
for achievement in achievements {
if achievement.0 == true {
imagesToShow.append(achievement.1)
}
}

Debugging code: carousel display string arrays

I'm coding a carousel that display a string array. The string element that is selected is to be displayed in a UIView.
var plotValue:Int = 0
var flag:Int = 1
let plotList: [String] = ["Phenotype","SNP","Synthesis","PheWAS","Circos"]
Basically, the user can swipe left or right the UILabel gets updated with a new value from the plotList.
Swipe left decrements a counter by -1, swipe right increments a counter by +1.
If the user reaches the initial value of the plotList[0] continues swiping left, the code will wrap around and start from the maximum element in the plotList.
If the user reaches the maximum value of the plotList and continues to swipe right, the code will wrap around and start from the plotList[0].
Once the users taps the UIView, another process is launched.
var swipeCarouselLeft: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "carouselLeft")
swipeCarouselLeft.direction = UISwipeGestureRecognizerDirection.Left
self.labelView.addGestureRecognizer(swipeCarouselLeft)
var swipeCarouselRight: UISwipeGestureRecognizer =
UISwipeGestureRecognizer(target: self, action: "carouselRight")
swipeCarouselRight.direction = UISwipeGestureRecognizerDirection.Right
self.labelView.addGestureRecognizer(swipeCarouselRight)
var tapButton:UITapGestureRecognizer =
UITapGestureRecognizer(target: self, action: "carouselTap")
self.labelView.addGestureRecognizer(tapButton)
Here are the functions defined.
func carouselLeft(){
AudioServicesPlaySystemSound(1052)
flag = -1
getLabel(flag)
}
func carouselRight(){
AudioServicesPlaySystemSound(1054)
flag = 1
getLabel(flag)
}
and
func getLabel(flag: Int){
plotValue = plotValue + flag
println("plotValue \(plotValue)")
switch plotValue {
case (plotList.count):
plotValue = 0
case (-1):
plotValue = plotList.count - 1
default:
plotValue = 0
}
UIView.animateWithDuration(2, animations: { () -> Void in
self.labelOutlet.textColor = UIColor.blueColor()
})
self.labelOutlet.text = plotList[plotValue]
println("\(plotList[plotValue])UIView")
}
func carouselTap(){
AudioServicesPlaySystemSound(1057)
}
Basically, when user swipe the resulting string is either the first or last element in the plotList array, none of the other elements in the array are shown.
Maybe I'm overthinking this and there's a simpler way to do this? or the Apple "preferred" way of doing this? or a more OOP??
Changed the logic to this and works and will wrap in both directions.
func getLabel(flag: Int){
if (flag == -1 && plotValue == 0){
plotValue = plotList.count - 1
}
else if (flag == 1 && plotValue == plotList.count - 1)
{
plotValue = 0
}
else
{
plotValue = plotValue + flag
}
UIView.animateWithDuration(2, animations: { () -> Void in
self.labelOutlet.textColor = UIColor.blueColor()
})
self.labelOutlet.text = plotList[plotValue]
}

Hide Background View from Table View Controller

I have just added a placeholder label to display text when my table view is empty however I can't figure out how to dismiss the background view once a new cell has been added.
This is how the table view looks once a cell has been added:
This is the code I am using to display the label:
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
println(arrayObject.paymentsArray().count)
if arrayObject.paymentsArray().count == 0 {
backgroundLabel.text = "You haven't added any transactions yet. Tap the add button to add a new transaction."
backgroundLabel.frame = CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height)
backgroundLabel.numberOfLines = 0
backgroundLabel.textAlignment = NSTextAlignment.Center
backgroundLabel.sizeToFit()
self.tableView.backgroundView = backgroundLabel
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
return 0
} else {
return arrayObject.paymentsArray().count
}
}
How can I dismiss the background label once a the arrayObject.paymentsArray().count does not equal 0?
EDIT:
I changed the code to this:
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
println(arrayObject.paymentsArray().count)
if arrayObject.paymentsArray().count == 0 {
backgroundLabel.text = "You haven't added any transactions yet. Tap the add button to add a new transaction."
backgroundLabel.frame = CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height)
backgroundLabel.numberOfLines = 0
backgroundLabel.textAlignment = NSTextAlignment.Center
backgroundLabel.sizeToFit()
backgroundLabel.hidden = false
self.tableView.backgroundView = backgroundLabel
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
return 0
} else {
backgroundLabel.hidden = true
return arrayObject.paymentsArray().count
}
}
This now hides the message as intended however the table view has lost the dividing lines in between the cells.
You need to keep a reference to your background label (say as a property of your viewController) and then set hidden equal to true e.g.
self.backgroundLabel.hidden = true on the label in your else clause.else
{
self.backgroundLabel.hidden = true
return arrayObject.paymentsArray().count
}
To get your lines back you need to set the separator style, you set it to none - you need single line.
{
self.backgroundLabel.hidden = true
return arrayObject.paymentsArray().count
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
}