Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff59d8dff8) - ios

What I'm trying to do:
The following bit of code is for 2 buttons on a page. This page is part of an array of pages (which is stored in self.parent; it also stores the index it has in that said array in self.index).
The 2 buttons are used to navigate forwards and backwards in this array of pages. If it tries to go forward but hits the end of the array, it should go back to the first page; Likewise for going back and hitting the beginning of the array.
#IBAction func previousButton(sender: AnyObject) {
self.prev()
}
#IBAction func nextButton(sender: AnyObject) {
self.next()
}
func next(){
if let _parent = self.parent{
if self.index != _parent.getSize()-1 {
self.view.addSubview(_parent[self.index+1].view)
}
else{
self.view.addSubview(_parent[0].view)
}
}
else{
println("This Page is not part of a List")
}
}
func prev(){
if let _parent = self.parent{
if self.index != 0 {
self.view.addSubview(_parent[self.index-1].view)
}
else{
self.view.addSubview(_parent[_parent.getSize()-1].view)
}
}
else{
println("This Page is not part of a List")
}
}
Problem is:
EDIT: The crashes have nothing to do with the if statements and it isn't random either; It crashes when I hit the next button for the tenth time, the previous button for the tenth time (ten is the number of pages I have in my list; if I add more pages, this number changes accordingly), and when I press prev after pressing next OR pressing next after pressing next.
I figured out the problem: I am not properly switching between pages, I am just adding one on top of the others; The crash occurs when I attempt to show a page that was already loaded (and still is somewhere beneath the stack of pages).
Now I need to find a better way to switch between them.

Make sure you have the same class name in StoryBoard and in ViewController

Related

how to prevent button showing up for split second when view loads

So my goal is to smoothly load the viewController with no split second bugs. I have a function that is used to determine what buttons to show when the view loads based off a field in a Firestore document. Here is the function:
func determinePurchasedStatusVerification() {
db.collection("student_users/\(user?.uid)/events_bought").whereField("event_name", isEqualTo: selectedEventName!).whereField("isEventPurchased", isEqualTo: true).getDocuments { (querySnapshot, error) in
if let error = error {
print("\(error)")
} else {
guard let querySnap = querySnapshot?.isEmpty else { return }
if querySnap == true {
self.purchaseTicketButton.isHidden = false
self.viewPurchaseButton.isHidden = true
self.cancelPurchaseButton.isHidden = true
} else {
self.purchaseTicketButton.isHidden = true
self.viewPurchaseButton.isHidden = false
self.cancelPurchaseButton.isHidden = false
}
}
}
}
I call this function in the viewWillAppear() of the vc but when I instantiate to that vc, this is the result...
The extra purchase ticket button shows up for a split second. Even though it's very quick, you can still see it and it's just not something a user would need to see. It's also the other way around when you click on a cell that's not purchased, the two bottom buttons show up for a split second. I just want to know how I can prevent this quick bug and be able to display a smooth segue with no delays in the button hiding. Thanks.
getDocuments is an asynchronous function, meaning it doesn't call its callback function immediately -- it calls it when it gets data back from the server. It may seem like a split second just because your internet connection is fast and the Firebase servers are definitely fast, but it's a non-zero time for sure. And, someone with a slower connection might experience much more of a delay.
Unless your callback is getting called twice with different results (which seems doubtful), the only solution here is to make sure that your initial state has all of the buttons hidden (and maybe a loading indicator) and then show the buttons that you want once you get the data back (as you are right now). My guess is, though, that you have an initial state where the buttons are visible, which causes the flicker.

How to wait for function to finish and update tableview before segue. iOS Swift

I am making an e-commerce app. On the basket view controller, when the user taps on the Checkout button, I'd like to check the availability of the items and update the basket accordingly when for example the stock is not enough, the quantity will be changed to available stock, or the product is removed from the basket when there's no stock available before segueing to another view controller. The problem is, I have a function that has a completion handler that is supposed to call the performSegue only when the function is finished and if there are no changes to the basket, however, it still goes to another view controller before the function is finished. Any advice on how to properly do this?
My completion handler was not done correctly that is why it was not working. But I finally got it to work with the following code
var changes = Bool()
func getBasketItems(completion: #escaping(_ success:Bool) -> Void) {
//each time there's an update to the database I set changes to true
changes = true
completion(changes)
}
I use it as the code below
self.getBasketItems { (success) in
if success {
// there are changes, update basket tableview
print("there are changes")
} else {
self.performSegue(withIdentifier: "goToMap", sender: self)
}
}

ButtonBarPagerTabStripViewController updateIndicator toIndex is wrong

I need to add or remove items from the navigation bar when the user changes screen so I have a code like:
override func updateIndicator(for viewController: PagerTabStripViewController, fromIndex: Int, toIndex: Int, withProgressPercentage progressPercentage: CGFloat, indexWasChanged: Bool) {
super.updateIndicator(for: viewController, fromIndex: fromIndex, toIndex: toIndex, withProgressPercentage: progressPercentage, indexWasChanged: indexWasChanged)
if indexWasChanged {
if toIndex == 2 {
rightBtn.alpha = 1
} else {
rightBtn.alpha = 0
}
}
}
rightBtn is simply a button on the right end of my navigation bar.
However during my testing runs I noticed that if the user taps on the ButtonBar instead of swiping to change screen, the fromIndex and toIndex is wrong. It will still be the values of the last change.
E.g. when the user swipes from the third screen to the second, the fromIndex will be 2 and toIndex 1 but if the user taps on the third button on the bar, the fromIndex will still be 2 even though it is expected to be at 1 the toIndex will also stay at 1 even though now it should be 2.
This results in my rightBtn not showing if the user swipes from the third screen then taps the bar back to the third.
Is there a way to obtain the correct toIndex during such a scenario or can it be fixed?
The first part is the buggy part where I navigate by tapping directly on the barbutton, the second part without the pointer is navigation by swiping with works fine, the Icon is only suppose to appear on the Third Tab
I have gotten a solution, for anyone with the same problem, there is a currentIndex in ButtonBarPagerTabStripViewController. So this:
if indexWasChanged {
if currentIndex == 2 {
rightBtn.alpha = 1
} else {
rightBtn.alpha = 0
}
}
will work over the original codes in the question.
Also I went to the GitHub project and found that someone else who is facing this problem already reported it. Hopefully it will be fixed and both methods will work.

Change View with NavigationViewController

all this is probably a trivial question, but I have not found a solution to it. I am making an app for Iphone using Swift.
I have a tableview with some strings and if I press a button I want to navigate back to the previous view directly. However, the code after my call
navigationController?.popViewControllerAnimated(true)
is always run, but I want the current activity to stop and go back to the previous view.
The code looks like:
#IBAction func DeletePressed(sender: UIButton) {
let deleteIndices = getIndexToDelete()
navigationController?.popViewControllerAnimated(true)
print("After navigationController")
for index in deleteIndices{
results?.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.removeAtIndex(index)
}
if (results?.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.count == 0){
results?.ListResults[yearShownIndex].months[monthShownIndex].day.removeAtIndex(dayShownIndex)
}
if (results?.ListResults[yearShownIndex].months[monthShownIndex].day.count == 0){
results?.ListResults[yearShownIndex].months.removeAtIndex(monthShownIndex)
}
if (results?.ListResults[yearShownIndex].months.count == 0){
results?.ListResults.removeAtIndex(monthShownIndex)
}
loadView()
}
"After navigationController" is always displayed.
In android you would start a new activity by creating intents to get the desired behaviour, but how does it work on Iphone?
My problem is that I want to be able to go back directly when navigationController.popViewControllerAnimated is called. This is just a toy example to understand how it works so that I can use it in the if-clauses later.
you could simply add a return statement after you pop the viewcontroller:
#IBAction func DeletePressed(sender: UIButton) {
let deleteIndices = getIndexToDelete()
navigationController?.popViewControllerAnimated(true)
return;
[...]
if you don't wants to execute code after "print("After navigationController")" then remove that code
or it is not possible to remove then toggle it when DeletePressed called

Why am I getting this error: fatal error - array index out of range?

I have 2 view controllers: a cameraVC and a detailVC. The camera is an AVFoundation camera that can capture and then select pictures, which then populates 4 small imageViews on my detailVC. I am trying to swipe between the two VCs so that when you hit 'yes' on the cameraVC, that then adds that to one of the 4 imageViews. I am currently using for-loops to achieve this, and below is the code I have in my detail VCs.
It works when I first load the app and take a few pics, but when I swipe back to my cameraVC and then back to my detailVC, the app crashes and logs error above.
In my code, I am attempting to set the imageViewArray to however many images there are in my imageArray. Then each time I go back to the detailVC, I am to clear my imageView.images and re-assign them. For some reason, it's not working. Any ideas why?
override func viewWillAppear(animated: Bool) {
if imageArray.count == 1 {
imageViewArray += [imageView1]
} else if imageArray.count == 2 {
imageViewArray += [imageView1,imageView2]
} else if imageArray.count == 3 {
imageViewArray += [imageView1,imageView2,imageView3]
} else if imageArray.count == 4 {
imageViewArray += [imageView1,imageView2,imageView3,imageView4]
}
for ima in imageViewArray {
if ima.image != nil {
ima.image = nil
}
}
for (index, imageView) in imageViewArray.enumerate() {
imageView.image = imageArray[index]
}
}
Add the line
imageViewArray = []
in the beginning of your viewWillAppear.
Or change your assignment to the imageViewArray in all 4 branches to something like:
imageViewArray = [imageView1,imageView2,imageView3,imageView4] // removed the "+"
The problem you are encountering here is that the method gets called when the view gets presented the first time AND when some presented view gets dismissed / popped again causing your view to appear again.
The result of that is that the imageViewArray still contains the imageviews from the last iteration. Adding the images again causes the array to get larger.
Assume that you had 3 images the first time, now the second time you have 4 images. Therefore you enter the last else section and add 4 new elements to the array, causing it to have 7 elements while the imageArray still only has 4. Now you iterate over all imageViewArray entries and try to access the imageArray at up to index 6 while the array only contains value until index 3 -> crash.
Note that it was annoying writing this answer because you have too many variables that all sound and look the same imageView, image, imageArray, imageViewArray, ima, imageViewX. Please try to find better names.

Resources