VC.isHidden = true not working as expected - ios

I have a Viewcontroller ThirdViewControllerPassenger which has multiple subviews on it, including a UICollectionView called collectionViewwith horizontally scrolling Cards. So far, so good. I have written code to be executed from a tap action from inside the uicollectionviewcells. Tapping the action does work and prints to console. However, by pressing one of these cards I want to hide the whole UICollectionView. I have set up an onTap Function as shown here:
#objc func onTap(_ gesture: UIGestureRecognizer) {
if (gesture.state == .ended) {
/* action */
if favCoordinate.latitude == 1.0 && favCoordinate.longitude == 1.0 {
//There has been an error OR the User has pressed the new Address button
//do
}else{
ThirdViewControllerPassenger().collectionView.isHidden = true
if ThirdViewControllerPassenger().collectionView.isHidden == true {
print("done!")
}
}
}
}
As you can see, I have already been troubleshooting a bit. I have tested ThirdViewControllerPassenger().collectionView.isHidden = true from ThirdViewControllerPassenger directly, which worked. It does not work, however, from a cell. The "done!" print never gets printed to console, so the call never arrives. I wonder why or what I am doing wrong.
Don't mind the first if statement, that function is not written yet. That should not matter. I am guessing that the rest of my code would not lead to any more clues.

Every ThirdViewControllerPassenger() here
ThirdViewControllerPassenger().collectionView.isHidden = true
if ThirdViewControllerPassenger().collectionView.isHidden == true {
print("done!")
}
is a new instance not the real one , you need to get access to the real shown istance
delegate.collectionView.isHidden = true
if delegate.collectionView.isHidden == true {
print("done!")
}

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.

Can't change UIView.isHidden property after action is called by UIBarButton

I am using UIView's isHidden property to show/hide controls on a posting interface I'm developing. I use this to change the controls available when the keyboard is up/down, post types are selected, etc. I have written a method triggered by notifications that sets the view properties (mainly .isHidden) and animates changes, which is called when the keyboard appears, or when the user selects a post type which then changes the controls available to them. This usually works perfectly fine, and the properties change as intended, but when the method is triggered by a UIBarButton action, the view being set to visible becomes unresponsive. After hitting bar button I am unable to change the isHidden property of the view (even by explicitly setting isHidden to true or false... it does not change).
Here is the method that changes the view properties:
#objc func animateActionViewChange(_ sender: Notification) {
// DEBUG
print("\n[BEFORE]\nnormalActionView.isHidden: \(normalActionView.isHidden)\nkeyboardActionView.isHidden: \(keyboardActionView.isHidden)\nkeyboardUp: \(keyboardUp)\nactionViewActive: \(actionViewActive)")
// save prev state
let normalTemp: Bool = normalActionView.isHidden
let keyboardTemp: Bool = keyboardActionView.isHidden
// set new state based on env vars
normalActionView.isHidden = keyboardUp ? true : !actionViewActive
keyboardActionView.isHidden = keyboardUp ? !actionViewActive : true
// DEBUG
print("[AFTER]\nnormalActionView.isHidden: \(normalActionView.isHidden)\nkeyboardActionView.isHidden: \(keyboardActionView.isHidden)\nkeyboardUp: \(keyboardUp)\nactionViewActive: \(actionViewActive)")
// animate opacity changes
if normalActionView.isHidden != normalTemp {
let targetAlpha: CGFloat = normalTemp ? CGFloat(1) : CGFloat(0)
UIView.animate(withDuration: actionViewAnimationDuration / 2) {
self.normalActionView.alpha = targetAlpha
}
}
if keyboardActionView.isHidden != keyboardTemp {
let targetAlpha: CGFloat = keyboardTemp ? CGFloat(1) : CGFloat(0)
UIView.animate(withDuration: actionViewAnimationDuration / 2) {
self.keyboardActionView.alpha = targetAlpha
}
}
}
and the action called by the UIBarButton (this issue concerns when the button title is 'back'):
// back/cancel button action
#IBAction func cancel(sender: UIBarButtonItem) {
if sender.title == "Cancel" {
// cancel ongoing request if there is one
if let request = ongoingRequest {
if !request.isFinished {
request.cancel()
}
}
self.performSegue(withIdentifier: "cancel_unwindFromNewPostView", sender: self)
} else {
// reset post type to default (general)
postType = .general
// set actionViewActive bool to set visibility going forwards
actionViewActive = true
// hide datePicker
self.datePickerView.isHidden = true
actionViewAnimationDuration = 0.35
NotificationCenter.default.post(name: NSNotification.Name("animateActionViewChange"), object: nil)
UIView.animate(withDuration: 0.35) {
// layout
self.view.layoutIfNeeded()
}
// revert button text to 'cancel'
cancelButton.title = "Cancel"
}
}
Here is the output of the debugging flag before the 'back' button is hit:
[BEFORE]
normalActionView.isHidden: false
keyboardActionView.isHidden: true
keyboardUp: true
actionViewActive: true
[AFTER]
normalActionView.isHidden: true
keyboardActionView.isHidden: false
keyboardUp: true
actionViewActive: true
after:
[BEFORE]
normalActionView.isHidden: true
keyboardActionView.isHidden: true
keyboardUp: false
actionViewActive: true
[AFTER]
normalActionView.isHidden: true
keyboardActionView.isHidden: true
keyboardUp: false
actionViewActive: true
As you can see above, the isHidden property of the normal action view is not changing, even though it is being set to 'false'. I originally thought this was because a reference was being lost somewhere (though I thought that would result in a nil reference, which I don't have), so I made the references to normalActionView and keyboardActionView strong. This did not fix the problem, obviously, so I put the changes to isHidden in a method called by a notification to ensure it was always on the same thread (which it is, I checked by printing the current thread, it is always main), but that did not help either.
isHidden behaves as though it is cumulative. For example, if you set it to true twice in a row, you would need to set it to false twice.
I usually use a check for the opposite value before setting a value.
if view.isHidden == false {
view.isHidden = true
}
Why did you bind uiview animation to keyboard notifications ? Is that a required part of your application UI ? I think you can animate your view after a button pressed. Because it's highly difficult to catch the exact keyboard animation speed together working with any view. What I mean is that keyboard closes fast while notification center tries to catch and animate view.
Thanks.

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

Swift update UILabel with dispatch_async not working

Why doesn't this work?
dispatch_async(dispatch_get_main_queue(), {
self.timeStringLabel.text = "\(self.timeStringSelected)"
println(self.timeStringLabel.text)
})
I'm trying to update a label in Swift but the UI for the label never changes. I keep googling it, but I can't find any responses that don't use dispatch_async. What am I doing wrong?
1st Edit: I was mistaken. I'm not printing the updated text. The text never changes. It always prints out Optional("0") if that helps. The default value is 0 as defined in the Storyboard.
I have tried it with and without dispatch_async without any success. I also tried adding
self.timeStringLabel.setNeedsDisplay()
Immediately after updating the text, but that also doesn't work.
Edit 2: Here's the complete function + UILabel declaration
#IBOutlet weak var timeNumberLabel: UILabel!
#IBAction func timeNumberButtonPressed(sender: UIButton) {
println("Number Selected. Tag \(sender.tag)")
dispatch_async(dispatch_get_main_queue()) {
self.timeNumberOneButton.selected = false
self.timeNumberTwoButton.selected = false
self.timeNumberThreeButton.selected = false
self.timeNumberFourButton.selected = false
if sender.tag == 0{
self.timeNumberSelected = 0
} else if sender.tag == 1 {
self.timeNumberSelected == 5
} else if sender.tag == 2 {
self.timeNumberSelected == 10
} else {
self.timeNumberSelected == 24
}
sender.selected = true
self.timeNumberLabel.text = "\(self.timeNumberSelected)"
self.timeNumberLabel.setNeedsDisplay()
println(self.timeNumberLabel.text)
}
}
The label is clearly visible as shown in this picture. I didn't think it would be this hard to implement, but I was very wrong. I'm willing to bet it's something really simple that I'm missing.
Try adding the line
self.timeStringLabel.setNeedsDisplay()
(after the label change)
Because the code is run asynchronously, the interface-updating methods may miss the change and not display it (especially if a time-consuming bit of code occurs before the label change). Adding this code forces the interface to check for and display any changes it may have missed.
This should be used after any asynchronous task that changes the interface, as the task's running may overlap with the interface methods, resulting in a missed change.
*Thanks to the iOS Development Journal

Why only Right gesture showing?

I've never used gestures before, and wrote this bit of code (Note: I am using Xamarin and iOS).
When I use the code below, the only value ever received is "Right", despite what I do in the simulator or on my actual iPhone. I'm stumped as to why. I figure I should either get nothing, or everything should work.
// Swipe gesture
this.View.AddGestureRecognizer(new UISwipeGestureRecognizer(sw =>
{
if (sw.Direction == UISwipeGestureRecognizerDirection.Left) {
txtTestLabel.Text = "Left";
}
else if (sw.Direction == UISwipeGestureRecognizerDirection.Right) {
txtTestLabel.Text = "Right";
}
else {
txtTestLabel.Text = sw.Direction.ToString();
}
}));
The default direction of the UISwipeGestureRecognizer is right (see the documentation). Set the direction property to left to track the left swipe.

Resources