Getting multiple IF statement clauses to work together - ios

I am building an iOS quiz app and depending on certain key words that appear in the question label I want the background photo to change corresponding to the key word in the question. For example, if the question label contains the word "food" I would want the background picture to always show a picture of an apple. If the question label contains the word "fruit" I also want the background picture to be that same apple as for the key word "food". However when I run my code it only works properly if I use only ONE key word.
//this code works and changes the background picture appropriately
func quizImage() {
if (questionLabel.text?.contains("food"))!
//applePicture is the name of the image
{ questionImage.image = applePicture }
}
However when I try the following ,to use multiple if clauses, the background photo doesn't change at all, even if one of the key words appear in the question label
func quizImage() {
//this code doesn't work and the background photo never changes
if (questionLabel.text?.contains("food"))!,(questionLabel.text?.contains("apple"))!
{ questionImage.image = applePicture}
}
Any help of advice is greatly appreciated!

If you use comma(,) then it will be true only if both the conditions satisfies. Do like this,
if let text = questionLabel.text, (text.contains("food") || text.contains("apple")) {
questionImage.image = applePicture
}

You could wrap those two variants in an array, and use the contains function:
if let text = questionLabel.text, ["food", "apple"].contains(where: { text.contains($0) }) {
questionImage.image = applePicture
}
This will reduce the code duplication, and it's scalable in case that later on you will need to cover more that 2 variants.

Related

Tableview reload not accurate

I have a controller, that allows the user to type in a TextField.
Every time the user types a character, the string in that textfield is compared to an array of strings. If there is a match, the resulting array is displayed in a uitableview.
Here's the code:
func searchAutocompleteEntriesWithSubstring(substring:String){
let SUBSSTRING = substring.uppercased()
autocompleteStrings.removeAll()
for thisSchool in schoolArray{
if(thisSchool.name?.uppercased() .contains(SUBSSTRING))!{
autocompleteStrings.append(thisSchool)
}
}
autocompleteTableView.reloadData()
}
Basically, this works fine. BUT!
If the user types rather fast, the autocompleteTableView displays one or more (empty) rows than there actually are strings in the autocompleteStrings array.
I tried encapsulating the above code in DispatchQueue.main.async {}, but that made things even worse.
I guess it has something to do with NeedsLayout or NeedsDisplay, but I've never really understood the mechanism behind it, and how/where to apply these.
I hope you can advise me
Try this code
func searchAutocompleteEntriesWithSubstring(substring: String) {
let filtered = schoolArray.filter() { ($0.name ?? "").uppercased().range(of: substring.uppercased()) != nil }
autocompleteStrings = filtered.map() { $0.name! }
autocompleteTableView.reloadData()
}
Maybe you need a lock?
1.in a async queue.
2.lock locks.
3.matching and array appending
4.lock unlocks.
5.reload in mainqueue

Get the current text inside of a textfield for IOS custom keyboard

I am developing a IOS custom keyboard. I was wondering if there was a way to fetch the current text inside of the text field and how it would work.
For example, we can use textDocumentProxy.hasText() to see if the textfield has text inside but I want to know the exact string that is inside the textfield.
The closest things would be textDocumentProxy.documentContextBeforeInput and textDocumentProxy.documentContextAfterInput. These will respect sentences and such, which means if the value is a paragraph, you will only get the current sentence. Users have been known to retrieve the entire string by repositioning the cursor multiple times until everything is retrieved.
Of course, you generally do not have to worry about this if the field expects a single value like a username, email, id number, etc. Combining the values of both before and after input contexts should suffice.
Sample Code
For the single phrase value, you would do:
let value = (textDocumentProxy.documentContextBeforeInput ?? "") + (textDocumentProxy.documentContextAfterInput ?? "")
For values that might contain sentence ending punctuation, it will be a little more complicated as you need to run it on a separate thread. Because of this, and the fact that you have to move the input cursor to get the full text, the cursor will visibly move. It is also unknown whether this will be accepted into the AppStore (after all, Apple probably did not add an easy way to get the full text on purpose in order to prevent official custom keyboards from invading a user's privacy).
Note: the below code is based off of this Stack Overflow answer except modified for Swift, removed unnecessary sleeps, uses strings with no custom categories, and uses a more efficient movement process.
func foo() {
dispatch_async(dispatch_queue_create("com.example.test", DISPATCH_QUEUE_SERIAL)) { () -> Void in
let string = self.fullDocumentContext()
}
}
func fullDocumentContext() {
let textDocumentProxy = self.textDocumentProxy
var before = textDocumentProxy.documentContextBeforeInput
var completePriorString = "";
// Grab everything before the cursor
while (before != nil && !before!.isEmpty) {
completePriorString = before! + completePriorString
let length = before!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(-length)
NSThread.sleepForTimeInterval(0.01)
before = textDocumentProxy.documentContextBeforeInput
}
// Move the cursor back to the original position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(completePriorString.characters.count)
NSThread.sleepForTimeInterval(0.01)
var after = textDocumentProxy.documentContextAfterInput
var completeAfterString = "";
// Grab everything after the cursor
while (after != nil && !after!.isEmpty) {
completeAfterString += after!
let length = after!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(length)
NSThread.sleepForTimeInterval(0.01)
after = textDocumentProxy.documentContextAfterInput
}
// Go back to the original cursor position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(-(completeAfterString.characters.count))
let completeString = completePriorString + completeAfterString
print(completeString)
return completeString
}

Using UIButton Text as Text Input - Swift

Hello I have a profilelbl variable as below which is a uibutton. I want the text of the button to be an input in my database (parse). But I couldn't figured it out. I tried lots of things but still getting error:
#IBOutlet weak var profileLbl: UIButton!
var notification = PFObject(className: "notifications")
notification["actionReceiverName"] = profilelbl.text /*not working*/
/* also tried
notification["actionReceiverName"] = sender.profilelbl.text
notification["actionReceiverName"] = profilelbl.title */
you can do it easy like that
if let button = profilelbl as? UIButton {
if let title = button.titleForState(.Normal) {
println(title)
notification["actionReceiverName"] = title
}
}
Using UI objects to save/load data is a very bad idea. Using user-visible strings programmatically is an even worse idea. #ÖzgürErsil answered the question you asked, but the better answer to your question is "Don't do that. Ever."
Here are 2 examples where your approach will fail:
If 6 months later you want to change your UI and rename your button,
you won't remember that the button title is used in code and your
code will break. To that you would have to alter your database to
use a different string value.
If you decide to localize your app for foreign
languages, the button titles will come up in the local language, and
your code will break. There is no clean way to fix this problem,
since each local language would use a different version of the
button title.
It would be better to put unique tag numbers on your buttons, then look up text strings using the tags and pass those strings to your database.
Say you have button tags starting at 100.
You'd use code like this:
let buttonStrings = ["button1", "button2", "button3"]
let baseButtonTag = 100;
#IBAction func handleButton(sender: UIButton)
{
let tag = sender.tag
if tag >= baseButtonTag && tag < baseButtonTag + buttonStrings.count
{
let index = sender.tag - baseButtonTag
let buttonString = buttonStrings[index];
//Now use buttonString with your database as desired.
}
}

Is there a way to compare button images in Swift/iOS

I've looked around and I don't think there's a way to do this, but what I'm looking for is the ability to compare button images. I have a situation where I want to see if the button is set to a certain image before I change it. The pseudocode would be something like
if (myCell.followButton.image == UIImage(named: "")) {
//do something here
}
Updated for iOS 8 thanks to kakubei
UIButton has a property called currentImage, so use that to compare the images:
iOS 8+
if myCell.followButton.currentImage.isEqual(UIImage(named: "yourImageName")) {
//do something here
}
iOS 7-
if (myCell.followButton.currentImage == UIImage(named: "yourImageName")) {
//do something here
}
A better way to achieve this functionality would be to keep track of the button's selected state and change its image based on that. That would make it more flexible if you ever change the name of the image.
Matt Cooper's solution won't work in iOS 8 because Apple has changed how images are cached. You now need to use .isEqual
As in:
// assuming both button and newButton are UIImage instances
if button.currentImage!.isEqual(newButton) {
...
From Apple Docs:
As of iOS 8, you can no longer rely on pointer equality to compare
cached UIImage objects as the caching mechanism may not return the
same UIImage object, but will have cached image data separately. You
must use isEqual: to correctly test for equality.
The downside of this is that it won't work in iOS 7! I don't know of a way that will work in both but I'm hoping there is, I'm searching for it myself :)
It can be done in following way:
if let btnImage = sender.image(for: .normal),
let Image = UIImage(named: "firstImage.png"), UIImagePNGRepresentation(btnImage) == UIImagePNGRepresentation(Image)
{
sender.setImage(UIImage(named:"secondImage.png"), for: .normal)
}
else
{
sender.setImage( UIImage(named:"firstImage.png"), for: .normal)
}
On iOS 13 #Matt Cooper's answer does not work, I'm not sure but other infos are included in the name of the image (maybe changes in the API since 2015), so my comparison always returns false:
UIImage: 0x28297e880 named (main: Icon-camera-alt) {20, 20}
I've used this in my case:
myButton.currentImage?.description.contains("camera-alt")
Sure, if you keep the image references you can just do a straight compare. Otherwise, I am not aware of a way.

Swift: Random number arrays inside images and variables with a for loop

I am creating a game in which, depending on the number of 'swipes' chosen to do, (let's say 3), 3 different patterns show on the screen, one by one. I am working on developing the first pattern.
So I have this:
if (swipes.no_of_swipes) == 3 {
swipeArray = Array<UInt32>(count: 3, repeatedValue: 0)
for i in 0 ..< 3 {
swipeArray[i] = arc4random_uniform(84)}
}
As far as I am aware, this code creates an array with three UInts which can be accessed by doing swipeArray[0], swipeArray[1], and swipeArray[2]. My first question is how long will this swipeArray stay the same? Until the close the view? Should I have a 'refresh button' when the user loses - and if so, how would I make one?
Then I have a property observer. You will notice the for loop, which I am using to keep code concise. I understand that I could do something like x++ somewhere in here so that it will go through each one.
var playBegin: Bool = false{
didSet {
if playBegin == true {
println("\(playBegin)")
var swipes = Menu()
if (swipes.no_of_swipes) == 3 {
for i in 0 ..< 3 {
patternRoom.image = UIImage(named: "pattern\(swipeArray[x])")
//rest of code
}
}
}
The pattern image comes from a set of 84 images named like pattern7 and pattern56. My second question is, how could I code the for loop to go through each swipeArray[x].
Thank you in advance,
Will
how long will this swipeArray stay the same?
This is a bit too open ended. It’ll stay the same until you assign a new value to it, either from this same bit of code or a different part. Only you can know when that will be, by looking at your code.
Since you express an interest in keeping the code concise, here’s a couple of code tips.
You might think about writing your first snippet’s loop like this:
swipeArray = (0..<swipes.no_of_swipes).map { _ in
arc4random_uniform(84)
}
This combines creating a new array and populating the values. By the way, just in case you don’t realize, there’s no guarantee this array won’t contain the same value twice.
It’s also probably better to make swipeArray of type [Int] rather than [UInt32], and to convert the result of arc4random to an Int straight away:
Int(arc4random_uniform(84))
Otherwise the UInt32s will probably be a pain to work with.
For your second for loop, you can do this:
for i in swipeArray {
patternRoom.image = UIImage(named: "pattern\(i)")
// rest of code
}
When writing Swift, usually (but not always), when you find yourself using array[x] there’s a better more expressive way of doing it.

Resources