UPDATE:
Should have read the reference documentation...
"A reference to the place picker must be retained for the duration of the place picking operation. If the retain count of the place picker object becomes 0, the picking operation will be cancelled and the callback will not be invoked."
Make sure your placePicker object is referenced in your UITableViewController class:
GOOD: placePicker = GMSPlacePicker(config: config)
BAD: let placePicker = GMSPlacePicker(config: config)
Original Problem:
I have a UIButton element inside of a UITableViewCell in Storyboard connected by IBAction to a function in my UITableViewController.
The function executes when I press the button, and configures the GMSPlacePicker object as instructed in Google's SDK Guide (https://developers.google.com/places/ios-api/placepicker).
Upon calling the pickPlaceWithCallback function, it seems like the Place Picker UI comes up and then immediately closes, taking the user back to the UITableViewController. The callback doesn't run at all so there's no error being passed back to help debug.
Anyone run into this problem?
Related
I have implemented a QR Reader using (AVFoundation) AVCapture Session in iOS. In my ParentViewController I have implemented a CollectionView. inside the collection view cell I have implemented QR Code Reader and I start the capture session start running inside the cell. it works fine and read the metadata output. I stop the session when metadata output delegates called. But If I leave the ParentView Controller the Capture Session is still running and when I navigate to another ViewController it capture the session from background. Why is this and how can I stop the capture session When navigating to another view controller.
Here is the Hierarchy,
ParentViewController --->(inside)CollectionView --->(inside) CollectionViewCell --->(inside) QR Reader with capture session Strat.
What Happens,
ParentViewController ----> (navigate to another controller) Capture Session Still Activated and reads qr codes from background
What I want,
ParentViewController ---->(navigate to another controller) Shouldn't capture anything.
I Tried In ParentViewController, inside viewWillDisappear
override open func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let qrreaderCell = QRReaderCell()
qrreaderCell.captureSession.stopRunning()
ColletionView.reloadItems(at: [IndexPath(row: 0, section: 0)])
}
but didn't work and failed. can anyone help with this.
You are creating new instance of QRReaderCell.
let qrreaderCell = QRReaderCell()
qrreaderCell.captureSession.stopRunning()
Instead of creating new instance, just access the current cell which contains the QRReader.
let qrreaderCell = collectionView.cellForItem(at: IndexPath(row: 0, section: 0))
qrreaderCell.captureSession.stopRunning()
And make sure your viewWillDisappear get called. and if not, you can stop the capture session right before you navigate to another viewController.
just stop the capture session right before you push or segue to another viewController.
It seems more likely there is a retain cycle, but tough to tell without seeing your cell configuration code.
To stop existing sessions, you should iterate your collection view and stop the session on any already existing cells. You could do this in viewDidDisappear (your approach created a new cell, which would be released anyways at the end of the method)
If you post the cell class and the controller class code perhaps it may be helpful.
I am working in Swift. When a user presses a UIButton it calls a function ButtonPressed(). I would like ButtonPressed() to do two things:
Update the UIView by removing the current buttons and texts, then uploading some new text.
Call function TimeConsumingCalculation(). TimeConsumingCalculation is the complicated part of my app and does some calculations which take about 20 seconds or so to complete.
Right now, I have the code in the basic order:
ButtonPressed(){
self.Button.removeFromSuperview()
TimeConsumingCalculation()
}
However, it will not remove the button or do any other UI updates or additions until after the TimeConsumingCalculation is complete. I have read and attempted a few guides on closures and asynchronous functions, but have had no luck. Is there a special property with UIView that is causing it to be updated last?
As a side note - I have already attempted putting all UI actions in a separate function and calling it first. It doesn't work. The time consuming function does not take any variables from the buttons or UI or anything like that.
Thanks!
It seems like timeConsumingCalculation() is blocking the main queue, which is in charge of UI updates. Try calling it like this instead and use the isHidden property to hide the button instead of removing it from the view completely.
ButtonPressed(){
self.Button.isHidden = true
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
self.timeConsumingCalculation()
}
}
here you call timeConsumingCalculation() asynchronously on a background thread. The quality of service we give it is userInitiated, read more about quality of service classes here
can anyone say how do i observe for action in Reactive Cocoa for a UIButton or UIControl..
An alternative way is to bind the view to the view model.And observe changes on the Mutable Property.
I tried using below code but none is firing.
self.rollBtn.reactive.trigger(for: .touchUpInside).observeValues {
value in
print(value)
}
EDIT: Actually i am trying to get the sender on button Tap..how can i do that?
You have done nothing wrong in this code snippet - trigger(for:) is one of the ways to get notified in RAC 5.0. It should print () for every press on the button.
Have you linked the button with the view, if you are using Storyboard or Interface Builder? Where did you place this piece of code? Make sure you place it in viewDidLoad or awakeFromNib so that it gets called before the view is presented.
-
EDIT: Actually i am trying to get the sender on button Tap..how can i do that?
As mentioned in the comments, trigger(for:) returns a Signal<(), NoError>. It doesn't include the sender with the value event. You would need to reference the sender manually, e.g.:
button.reactive
.trigger(for: .touchUpInside)
.observeValues { [unowned button] in
_ = button
}
Here is the scenario:
OrderVC have a table view on which if you right swipe it shows some
options. In which one of the option is Checkout.
When user taps on Checkout it opens up CheckoutVC which has a parent class OrderVC.
Here user can add some text and can attach multiple images and can also save this data as draft which is achieved using core data. But when user submit the bill I'm using AFNetworking to call web api and upload images using AFMultipartFormData. All of this process is taking place on a background thread i.e.dispatch_async
I can't update UI in dispatch_get_main_queue because methods are calling other method from within see this question it'll clear this point. So it calls the update UI right after first method is finished.
Question
As long as background thread is working it should show activity indicator on the cell. When it's finished and the response is success in CheckoutVC it should reload the tableView of OrderVC.
Solution I triedI tried to run a for loop in allOrderID which are the ID's I get via web api hit of active orders. Then I made a call to MR_findFirstByAttribute to find if any of the fetched OrderID exist in drafts. There is an attribute isSending in DraftOrderInfo entity which is a BOOL and I truns it to true when checkout enters background thread. So if isSending is true I show the activity indicator in place of a UIView I created.
for (NSString *orderID in allOrderId) {
DraftOrderInfo *dpi = [DraftOrderInfo MR_findFirstByAttribute:#"orderID" withValue:orderID];
if (dpi.isSending) {
orderCell.rightUtilityButtons = nil;
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityView.center = CGPointMake(orderCell.orderStatusIndicatorBadgeView.frame.origin.x-2, orderCell.orderStatusIndicatorBadgeView.frame.origin.y-8);
[activityView startAnimating];
[orderCell.orderStatusIndicatorBadgeView addSubview:activityView];
}
}
The output I get is that when OrderVC is loaded it started showing activity indicator on all the cells.
First, your following point is not valid. Read the comments on the question:
can't update UI in dispatch_get_main_queue because methods are calling
other method from within see this question it'll clear this point. So
it calls the update UI right after first method is finished.
Back to the problem. There is multiple good practices to use. One of them:
Add the UIActivityIndicator to the UITableViewCell at design time(nib or storyboard) as done with the UILabel(s) and other controls.
On submitting the checkout. Change the isSending status to YES and inform the parent UIViewController to reload the table data by calling reloadData method on the UITableView.
In cellForRowAtIndexPath or willDisplay:forRowAt method set the state of the activity indicator as animating or stopped based on the isSending value. This way even if reloading the table or scrolling up and down, the activity indicator will have its state right.
Whe the submitting finishes. Change the isSending state to YES and inform your parent ViewController to reload the table by calling reloadData method on the UITableView. And since the checkin finishes in a background thread you should inform your parent ViewController using dispatch_get_main_queue. read the comments on the question you added to your question. This point you mention regarding the dispatch_get_main_queue is wrong.
I have a UITableViewController displaying a list of audio files, when tapping on an audio file I push another UITableViewController showing a detailed view of the audio file. This view also features a "Download" button and a UIProgressIndicator view.
I have a Download Manager class (implemented as a Singleton) which takes care of downloading the file. Its download method takes a block which is called with updates about the download progress. I'm using it to update the UIProgressIndicator view. This works fine up to the point where you leave the detail view controller and come back at a later point in time when the file is still downloading. Of course, the progress block specified earlier is still available, but the referenced UIProgressIndicator view inside of it is not, thus it is not updated anymore.
I'm wondering, if it's a sensible idea to just re-set the block upon re-entering the detail view controller (viewDidLoad) or if the block-based approach is not really suitable for this case? Maybe it'd be better to use KVO?
Any suggestions?
Thanks!
The block approach is useful if the lifespan of the downloader is controlled by the VC. This way, when the VC is released, it releases the downloader (the downloader would not be a singleton).
If not you risk creating captured objects (the VC) that cannot be released because they are referenced in your block, and your block is referenced by an "eternal" object (the singleton).
As the VC lifespan is potentially shorter than the downloader, a better option would be to use some subscription-based observing of the downloader singleton.
This way your VC subscribes in (e.g.) viewWillAppear and unsubscribes in viewWillDisappear (important).
You can also use a global progress notification (via NSNotificationCenter), key-value observing or any other means.
The important part is that when your VC is released, nothing in the downloader points to it.