How to remove iOS 11 search bar from view and update UI - ios11

In xcode 9 when I put the search bar in view its moves the rest of the UI down automatically. When I try to remove the searchbar from view I get a black space instead of the UI being corrected. The line I am using is: self.navigationItem.searchController = nil. I tried many things but I do not know how to correct the UI after the search bar is removed. Only when I move to another view controller and back the UI is correct again without the search bar. What am I missing here?
code:
#IBAction func searchIconPressed(_ sender: UIBarButtonItem) {
//ios 11
if #available(iOS 11.0, *) {
if self.navigationItem.searchController == nil {
self.navigationItem.searchController = self.searchController
self.searchController.isActive = true
}
else {
self.navigationItem.searchController?.isActive = false
self.navigationItem.searchController = nil
}
}
//when ios 9-10
else {
if self.navigationItem.titleView == nil {
self.navigationItem.titleView = self.searchController.searchBar
self.searchController.isActive = true
}
else {
self.navigationItem.titleView = nil
}
}
}
}

The answer for removing the search bar from view and make UI move up again:
self.navigationItem.searchController?.isActive = false
let search = UISearchController(searchResultsController: nil)
self.navigationItem.searchController = search
self.navigationItem.searchController = nil

And here for Objective C
[self.navigationItem.searchController setActive:NO];
UISearchController *nilSearch = [[UISearchController alloc] initWithSearchResultsController:nil];
self.navigationItem.searchController = nilSearch;
self.navigationItem.searchController = nil;

Related

Save button states when we move segmented control

We have for example segmented control, and array of buttons. If we select one of button in one segment - button need to save state, and than we move to another segment and choose another button. but if we back to previous button - it should be in the position we chose earlier.
that is, each of the segments must store the state of the buttons.
how to do it better?
struct SegmentsModel {
let title: String
var answered: Bool
var buttonIndex: Int? = nil
}
#objc private func buttonsTapped(_ sender: UIButton) {
let _ = buttons.map({$0.isSelected = false})
sender.isSelected = true
guard let index = buttons.firstIndex(of: sender) else { return }
switch index {
case 0:
selectedSegment(segmentedControl, buttonSelected: true, indexButton: 0)
case 1:
selectedSegment(segmentedControl, buttonSelected: true, indexButton: 1)
case 2:
selectedSegment(segmentedControl, buttonSelected: true, indexButton: 2)
case 3:
selectedSegment(segmentedControl, buttonSelected: true, indexButton: 3)
default:
break
}
}
#objc private func selectedSegment(_ sender: UISegmentedControl,
buttonSelected: Bool, indexButton: Int) {
let currentIndex = sender.selectedSegmentIndex
if buttonSelected == true {
buttons[indexButton].isSelected = true
} else {
let _ = buttons.map({$0.isSelected = false})
}
switch currentIndex {
case 0:
arrayOfSegments[0].answered = buttonSelected
arrayOfSegments[0].buttonIndex = indexButton
case 1:
arrayOfSegments[1].answered = buttonSelected
arrayOfSegments[1].buttonIndex = indexButton
case 2:
arrayOfSegments[2].answered = buttonSelected
arrayOfSegments[2].buttonIndex = indexButton
default:
break
}
}
As I mentioned in my comments, there are many ways to achieve what you want. I will share one idea with you.
I see that you tried to store all your buttons in an array and had to keep looping over them to figure out which button was selected previously. Since you want the code to have simpler logic, I will give you a different idea.
One way to achieve what you want is using tags. Every UIView object has a tag and the default is set to 0.
Using storyboard I set the tag of the buttons to 100, 101, 102, 103 and this can be done programmatically also.
You can choose any integers that you like but it is important to give them some unique numbers so when you try to get a view by tag, you get only the view you want.
So after setting the tags, this is what I updated in the code.
struct SegmentsModel {
let title: String
var answered = false
var buttonIndex: Int? = nil
{
// Auto-update the value of answered when value of buttonIndex changes
didSet
{
answered = buttonIndex != nil
}
}
}
I did not do any major updates here. I only added a property observer so when the button index gets set, the answered variable also is auto set so you don't have to anything more for this
Next, below is all the logic to manage the segments and the buttons. I used UIViewController with a StoryBoard so you might need to ignore somethings.
All the important code has comments so you can follow along.
class ViewController: UIViewController {
// I will store the results in this array
var storedResults: [SegmentsModel] = []
// Segment control outlet
#IBOutlet weak var segmentControl: UISegmentedControl!
// Array of buttons
#IBOutlet var buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
// Loop over all segments
for index in 0 ..< segmentControl.numberOfSegments
{
// Initialize and store default results values in storedResults array
if let segmentTile = segmentControl.titleForSegment(at: index)
{
storedResults.append(SegmentsModel(title: segmentTile))
}
}
}
// Segment action
#IBAction func selectedSegment(_ sender: UISegmentedControl)
{
// Update the UI of buttons
reloadButtons()
}
#IBAction func buttonTapped(_ sender: UIButton)
{
// Reset your buttons
let _ = buttons.map({$0.isSelected = false})
// Set the current button to selected
sender.isSelected = true
// Get the current result so we can update it
var currentResult = storedResults[segmentControl.selectedSegmentIndex]
// Update the current segments selected button index using tag
currentResult.buttonIndex = sender.tag
// Put the result back into the array as structs as value types
storedResults[segmentControl.selectedSegmentIndex] = currentResult
}
// Reload button data
private func reloadButtons()
{
// Reset your buttons
let _ = buttons.map({$0.isSelected = false})
let currentResult = storedResults[segmentControl.selectedSegmentIndex]
// Check if current index has a selected button and if it does retrieve it
// with a tag
if let selectedButtonIndex = currentResult.buttonIndex,
let selectedButton = view.viewWithTag(selectedButtonIndex) as? UIButton
{
// Show the selected button in the UI
selectedButton.isSelected = true
}
}
}
The end result achieved can be seen in this youtube video which I believe is what you wanted.

Can i use the class below by other ViewController's class by clicking dots at Page Controller?

There are 4 views:
DataViewController, GraphViewController and InfoViewController. PageViewController is made for switching between 3 others.
I've described everything to swipe my views by gestures. It works.
However, how can I use it in another view by clicking dots at "Page Control" object from any of 3 views?
import UIKit
class PageViewController: UIPageViewController
{
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.ListedViewController(name: "Data"),
self.ListedViewController(name: "Graph"),
self.ListedViewController(name: "Info")]
}()
override func viewDidLoad()
{
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
}
private func ListedViewController(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "\(name)ViewController")
}
}
// MARK: UIPageViewControllerDataSource
extension PageViewController: UIPageViewControllerDataSource
{
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController?
{
guard let viewControllerIndex = orderedViewControllers.index(of: viewController)
else
{
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0
else
{
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex
else
{
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController?
{
guard let viewControllerIndex = orderedViewControllers.index(of: viewController)
else
{
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else
{
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else
{
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = orderedViewControllers.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
Should i implement the object
Page Control
by: sender: PageViewController?
First of all ad UIPageViewControllerDelegate and UIPageViewControllerData source to our class as follows:
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
Then we add the following function, this is used to load the view controllers as you scroll through the different views.
func newVc(viewController: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: viewController)
}
Now above view did load add the following, the orderedViewControllers holds an array of View Controllers. If you want to add another View Controller to your page controller, this is the place to add it. We are adding in them by the storyboard identifiers sbBlue and sbRed we added earlier.
lazy var orderedViewControllers: [UIViewController] = {
return [self.newVc(viewController: "sbBlue"),
self.newVc(viewController: "sbRed")]
}()
(sbBlue and sbRed are identifiers in storyboard for each View)
Now add the following functions. These are used when your swiping through the page view controllers to load up the next view controller. This is configured so when you get to the last one, it will loop back to the start. If you swipe left on the first one it will load up the last one.
If you don’t want it to loop simply follow the instructions in the code to uncomment return nil and removing two lines of code. This is included in the code below.
// MARK: Data source functions.
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
// User is on the first view controller and swiped left to loop to
// the last view controller.
guard previousIndex >= 0 else {
return orderedViewControllers.last
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
// User is on the last view controller and swiped right to loop to
// the first view controller.
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
// Uncommment the line below, remove the line above if you don't want the page control to loop.
// return nil
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
Now in viewDidLoad() add the following:
self.dataSource = self
// This sets up the first view that will show up on our page control
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
**
Adding the page dot indicators
**
To add the page dot indications add a pageControl as follows above the viewDidLoad()
var pageControl = UIPageControl()
Now add the following function. This will position the page control at the bottom of the screen. The current page indication will be black, and the rest of the indicators will be white. You can change these to suit the design of your app.
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 50,width: UIScreen.main.bounds.width,height: 50))
self.pageControl.numberOfPages = orderedViewControllers.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.black
self.pageControl.pageIndicatorTintColor = UIColor.white
self.pageControl.currentPageIndicatorTintColor = UIColor.black
self.view.addSubview(pageControl)
}
Now in viewDidLoad() add these two lines:
self.delegate = self
configurePageControl()
And add the following function, this will make sure the page control indicator changes to the correct page as you scroll through.
// MARK: Delegate functions
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
}
Now run the app and you can see the page indicator as follows:

CloudKit nil optional error

I am trying to load, then modify and resave an array.
Here is code, modify func is the top one:
func modifyUserGroupsRequestee(){
print("step2")
acceptedUsersArray.append(groupNameLbl.text!)
//error
userGroupRecordToUpdate.setObject(acceptedUsersArray as CKRecordValue?, forKey: "userGroups")
database.save(recordToUpdate) { (savedRecord, error) in
if error != nil{
print(error.debugDescription)
}else{
print("SAVED RECORD")
}
}
}
func resaveUserGroups(){
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
//operation.resultsLimit = CKQueryOperationMaximumResults
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil{
self.userGroupRecordToUpdate = record
// self.acceptedUsersArray = (record.object(forKey: "userGroups") as! Array)
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
// self.feedTableView.reloadData()
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
// }
//self.feedTableView.reloadData()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
//self.tableView.reloadData()
// print(leaderboardInfo.count)
}
}
The function prints step1 but never gets to step2. In the bottom function, I have an if let statement I tried to create to solve my nil issue (I commented my previous code above that line- self.acceptedUsersArray... Anyway, I believe I am implementing the if let statement incorrectly, because no data is loaded, even though there is data in cloud kit.
And I do have my personal user cloudKit records set up, here's a pic:
You should try to keep your code always indented consistently.
(In Xcode editor, Cmd+A (Select All), then Ctrl+I (Re-Indent).)
With confusing comments removed, your resaveUserGroups shows as:
func resaveUserGroups() {
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
self.userGroupRecordToUpdate = record
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
}
}
Omitting some parts to clarify:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
database.add(operation)
}
}
The line database.add(operation) exists inside the recordFetchedBlock.
You may need to fix some more parts (that's another story), but at least, you need to move the line out of the closure to execute the operation you have created:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
//database.add(operation)
} //↓
database.add(operation)
}
I just solved it. Apparently there always needs to be some kind of value inside the array even if it was just created. I was trying to query an array that existed in the recordType, but not yet under the specific record.

How would I view the next image in an array of images?

In this function, I need to change the image of the image view, moving it onto the next image in the array.
private func updateImage(index: Int) {
for x in 0..<urlArray.count
{
let imageUrl:URL = URL(string: "\(urlArray.object(at: x) as! String)")!
let request:URLRequest = URLRequest.init(url: imageUrl)
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: { (response, imageDta, error) in
if (error == nil)
{
self.imagesArray.add(UIImage(data: imageDta!)!)
if self.imagesArray.count > 0
{
self.iv.image = self.imagesArray.object(at: 0) as! UIImage
}
}else{
print("ERROR - \(error!)")
}
})
}
}
However, currently, the image being used is set as the image at index 0 in my array. How would I move onto the next image if this function is called multiple times?
This is the line that I mainly need help with:
self.iv.image = self.imagesArray.object(at: 0) as! UIImage
UPDATE: I've tried to add the code Charly Pico suggested to my functions but it doesn't seem to be moving onto the next image in the array, I think it's to do with how it's being called. I'm going to go into detail in how my file is constructed and works to hopefully clarify how I want to use the change image code.
var urlArray:NSMutableArray! = NSMutableArray()
var imagesArray:NSMutableArray! = NSMutableArray()
These arrays which hold the URL's and Images are outside of any function.
func GetTheStories() {
let ref = FIRDatabase.database().reference().child("Content")
ref.observe(FIRDataEventType.value, with: {(snapshot) in
self.fileArray.removeAll()
if snapshot.childrenCount>0{
for content in snapshot.children.allObjects as![FIRDataSnapshot]{
let contentInfo = content.value as? [String: String]
let contentType = contentInfo?["contentType"]
let storyLink = contentInfo?["pathToImage"]
let content = storyLink
self.urlArray = NSMutableArray.init(array:[storyLink])
}
}
})
}
This is the function I call to append the URL's from my Firebase server to the URLSARRAY.
override func viewDidAppear(_ animated: Bool) {
for x in 0..<urlArray.count
{
let imageUrl:URL = URL(string: "\(urlArray.object(at: x) as! String)")!
let request:URLRequest = URLRequest.init(url: imageUrl)
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: { (response, imageDta, error) in
if (error == nil)
{
self.imagesArray.add(UIImage(data: imageDta!)!)
if self.imagesArray.count > 0
{
self.iv.image = self.imagesArray.object(at: 0) as! UIImage
}
}else{
print("ERROR - \(error!)")
}
})
}
}
This is the function Charly created and explained which allows me to set the initial image on the ImageView when the screen loads
private func updateImage(index: Int) {
var imageToPresent = 0
for x in 0..<urlArray.count
{
let imageUrl:URL = URL(string: "\(urlArray.object(at: x) as! String)")!
let request:URLRequest = URLRequest.init(url: imageUrl)
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: { (response, imageDta, error) in
if (error == nil)
{
self.imagesArray.removeAllObjects()
self.imagesArray.add(UIImage(data: imageDta!)!)
if self.imagesArray.count > 0
{
for x in 0..<self.imagesArray.count
{
if self.iv.image == self.imagesArray.object(at: x) as! UIImage
{
imageToPresent = x + 1
if imageToPresent >= self.imagesArray.count
{
imageToPresent = 0
}
}
}
self.iv.image = self.imagesArray.object(at: imageToPresent) as! UIImage
}
}else{
print("ERROR - \(error!)")
}
})
}
}
This is the function being called to update the image to the next one in the array. However, I believe it's not working either because my attempt to apply Charly's code below is not correct. This function is called in the ViewDidLoad like so
updateImage(index: 0)
And it is called to update the image here. This function is called when the Segmented progress bar changes to the next one.
func segmentedProgressBarChangedIndex(index: Int) {
print("Now showing index: \(index)")
updateImage(index: index)
}
I know it's a bit confusing as to why I'm not using buttons and actions, this is mainly because I'm trying to create an Instagram-style story, with segments that when finished, or the screen is tapped, change the image to the next in the array. I know there's a lot to look over but I think this will explain how I want to use the images array in greater detail.
so, lets say that your imagesArray contains 2 images, what I recommend doing is the following:
//Create a #IBAction and attach it to a UIButton in your Storyboard as a touchUpInside action.
#IBAction func changeImage()
{
//Set a variable with Int as 0.
var imageToPresent = 0
//Create a for to check image by image inside the array.
for x in 0..<imagesArray.count
{
//Compare the image at self.iv with the image in imagesArray at index x. And if the images are the same add a + 1 to imageToPresent.
if self.iv.image == imagesArray.object(at: x) as! UIImage
{
imageToPresent = x + 1
//Check if imageToPresent isn't bigger or equal than imagesArray, otherwise we will get an error and the App will crash.
if imageToPresent >= imagesArray.count
{
//If imageToPreset if bigger or equal to imagesArray.count, it means that there are no more images at a higher index, so we return imageToPresent to 0.
imageToPresent = 0
}
}
}
//Set the new image of imagesArray at index imageToPresent as UIImage to self.iv.
self.iv.image = imagesArray.object(at: imageToPresent) as! UIImage
}
Every time you want to change self.iv image you need to touch the button for the action to be triggered.
UPDATE
So I added a NSTimer to simulate that in 3 seconds your Progress View finishes animating, after the 3 seconds it rotates the image to another one, you can add this code after the for that loads all the images from the urls:
Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { (timer) in
self.changeImage()
}
And first of all update your urlArray to this:
urlArray = NSMutableArray.init(array: ["https://firebasestorage.googleapis.com/v0/b/motive-73352.appspot.com/o/Content%2F20170525130622.jpg?alt=media&token=1e654c60-2f47-43c3-9298-b0282d27f66c","https://www.videomaker.com/sites/videomaker.com/files/styles/magazine_article_primary/public/articles/18984/363%20C03%20Lighting%20primary.png"])
So you have at least 2 images to make the image rotation.
change the line to use the index being passed into your function like this
self.iv.image = self.imagesArray.object(at: index) as! UIImage
This function is trying to do a lot of things at once, so it's hard to tell how to make it do what you want, but, you do have an index parameter. Using that instead of the 0 here:
if self.imagesArray.count > index
{
self.iv.image = self.imagesArray.object(at: index) as! UIImage
}
Would wait for that image to load and then set that to the view.
NOTE: you need to check that the index is within bounds

Check for identical values swift

I'm trying to make a memory game where I apply images to 12 different buttons and check if the images are the same when 2 buttons are showing.
-------------------FINISHED FORM?-------------------------
Here is a try at Duncan C's suggestion;
func setImages() {
var values = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6]
values.shuffleInPlace()
button1.tag = values[0]
button2.tag = values[1]
button3.tag = values[2]
button4.tag = values[3]
button5.tag = values[4]
button6.tag = values[5]
button7.tag = values[6]
button8.tag = values[7]
button9.tag = values[8]
button10.tag = values[9]
button11.tag = values[10]
button12.tag = values[11]
}
#IBAction func buttonPressed(sender: UIButton) {
var images : [UIImage] = [
UIImage(named:"ye1")!,
UIImage(named:"ye2")!,
UIImage(named:"ye3")!,
UIImage(named:"ye4")!,
UIImage(named:"ye5")!,
UIImage(named:"ye6")!,
UIImage(named:"ye7")!,
UIImage(named:"ye8")!,
UIImage(named:"ye9")!,
UIImage(named:"ye10")!,
UIImage(named:"ye11")!,
UIImage(named:"ye12")!
]
images.shuffleInPlace()
let integrera = (sender.tag - 1)
let imageString:String = String(format: "ye%i", integrera)
if !firstButtonAlreadyPresssed {
firstButtonValue = sender.tag //remember the button for later
firstButtonAlreadyPresssed = true
sender.setImage(UIImage(named: imageString), forState: .Normal)
}
else
//We already have a first button pressed.
if sender.tag == firstButtonValue {
sender.setImage(UIImage(named: imageString), forState: .Normal)
}
else {
let secondimage = (sender.tag)
let secondString : String = String(format: "ye%i", secondimage)
sender.setImage(UIImage(named: secondString), forState: .Normal)
}
firstButtonAlreadyPresssed = false //reset the "isFirstButton" flag for the next time.
}
}
Hmm, there are a bit of lengthy and repetitive code here I believe. You can just add all buttons to the same #IBAction func. So you can have one #IBAction func buttonPressed(sender: UIButton) rather than 12 like you have now (I think, since your function is called #IBAction func button1PRESSED(sender: AnyObject)). So when you first click a button you store the button, and also store if its the first or second button clicked. When you click the second button you check if it has the same UIImage as the first one clicked, otherwise you what ever it is you are supposed to do.
#IBAction func buttonPressed(sender: UIButton) {
if self.firstButtonStored == true {
if self.firstButton.image == sender.image {
// They are the same
} else {
// they are not the same
}
} else {
self.firstButtonStored = true
self.firstButton = sender
}
}
I would also recommend storing all the buttons in one OutletCollection (works like an array) rather than 12 buttons in self. And I would also use something else than UIImage to check if they are the same, not sure if this actually works since you need the image name and not the image itself to make a comparison. Let me know if you need help.
Don't, dont, DON'T create 12 separate variables, card1-card12 and 12 IBActions button1PRESSED-button12PRESSED. This kind of thing where you have lots of exactly the same thing where the only thing that changes is a value is a "code smell". It tells you you are doing it wrong.
Instead, you should find a way to do it where you can use the same IBAction for all the buttons, and index into the array rather than having 12 separate variables.
I would suggest using tags on your buttons. Have button 1 use tag 1, button 2 use tag 2, etc.
Then in your IBAction, fetch the tag from the button and use that to figure out which button was pressed and what to do.
You will also need a flag that tells you if this was the first button pressed or the second, so you can tell if you need to test for matches (if it's the 2nd one) or just remember which one was pressed (if this is the first button pressed.)
var firstButtonValue: Int
var firstButtonAlreadyPresssed: Bool
#IBAction func buttonPressed(sender: UIButton)
{
if !firstButtonAlreadyPresssed
{
firstButtonValue = sender.tag //remember the button for later
firstButtonAlreadyPresssed = true
}
else
{
//We already have a first button pressed.
if sender.tag == firstButtonValue
{
//right answer. Handle it.
}
else
{
//wrong answer. Handle it.
}
firstButtonAlreadyPresssed = false //reset the "isFirstButton" flag for the next time.
}
}
You could have an array of the images to use for each value, and then fetch that image based on the value of firstButtonValue or sender.tag.
An approach, not a full solution
Create a struct Card with value and index properties and make it Equatable by value.
You can add more properties like done, status, image or whatever.
struct Card : Equatable, CustomStringConvertible {
let value : Int
let index : Int
var description : String { return "Card \(index)"}
}
func ==(lhs: Card, rhs: Card) -> Bool
{
return lhs.value == rhs.value
}
Shuffle the values and create an array of 12 cards
var values = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6]
values.shuffleInPlace()
var cards = [Card]()
for (index, value) in values.enumerate() {
cards.append(Card(value: value, index: index))
}
Implement a method to find the corresponding card
func twinCard(card : Card) -> Card {
return cards.filter({$0 == card && $0.index != card.index}).first!
}
In Interface Builder assign tags from 0 to 11 to the twelve buttons and connect all buttons to the same #IBAction
#IBAction func buttonPressed(sender: UIButton) {
let tag = sender.tag
let chosenCard = cards[tag]
let otherCard = twinCard(chosenCard)
// The following depends on the design of the UI
// for example get the corresponding button by viewWithTag(otherCard.index)
// or use an array which holds the button references.
...
}
Don't shuffle the view, shuffle the model ;-)

Resources