How to trigger method in a view from other view - flutter

I have two different views. In first view, I am listening for an event, whenever I receive the event I want to trigger a method in different view. Is this achievable? How can this be achieved, do I need to use a stream for this? I am using provider to share data and usually I access it like
context.read<Item>().doSomething
class View1 {
final Location _location = Location();
_location.onLocationChanged.listen((event){
print(event); // After receiving the event, I want to trigger the method in view2
}
}
class View2 {
void method() {
print('I want to be called when event is received from view1')
}
}

I recommend you to explore the Observer programming pattern.

Related

Collection View is reloaded then why wrong item disappears

I was seeing a code where a collection view is made and we can mark some items as favorite. Also there is a filter button on top if we select that only favorite items will be visible.
Now is favorite filter is applied, then any random item is unfavourated then from the collection view, the last item disappears (which is wrong, but the bussiness logic going on is right I have tested multiple times using breakpoints) and now if I go back and come to favorites page again then the wrong item which disappeared is in the list and the correct item which was unfavorited is not present because the businesses logic was right and the data source has correct elements so the collection view made is correct (what i think).
Now I guess after tapping favorite button something wrong is hapenning, that the collection view reloading has some problem.
Also second question is is always mandatory to call
collectionView.reloadData()
and
collectionView.reloadItems(at: [IndexPath(item: index, section: 0)])
in main thread using DispatchQueue.main.async
I am new to swift, can you help out what could be the case that the reload is happening is wrong but in data source correct element is removed.
Problem gif: View Here
I was not able to add in this post
UPDATE: added code snippets:
On tapping heart symbol this function is invoked:
func editFavouriteChannelList(with viewModel: MyChannelViewModel?, index: Int) {
guard var viewModel = viewModel,
viewModel.isFavouriteEnabled,
var channel = viewModel.channels?[safe: index],
!channel.state.isChangingStateInProgress() else {
return
}
// Some working
// Calls updateCell fn with same viewModel but updated channel state
self.updateCell(with: viewModel, channelCellModel: channel)
// This makes API call then again we call on it's success updateCell fn with viewModel have removed that updated channel
interactor.editFavouriteChannelList(with: channel, isMarkingFavourite: isMarkingFavourite)
}
This presenter's updateCell is called twice,
first when only channelCellModel is updated
and second time after making an API call that time viewModel will have that channel removed from it.
In presenter:
private func updateCell(with viewModel: MyChannelViewModel, channelCellModel: MyChannelCellModel) {
DispatchQueue.main.async { [weak self] in
self.interface?.updateCell(with: viewModel, channelCellModel: channelCellModel)
}
}
In ViewController:
func updateCell(with viewModel: MyChannelViewModel, channelCellModel: MyChannelCellModel) {
if myChannelVM?.channels?.count != viewModel.channels?.count {
self.myChannelVM = viewModel
collectionView.reloadData()
} else {
guard let index = myChannelVM?.channels?.firstIndex(of: channelCellModel) else {
return
}
myChannelVM?.channels?[index] = channelCellModel
print(" Like: updateCell local : \(myChannelVM?.channels?[index])")
collectionView.reloadItems(at: [indexPath])
}
}
In View Controller logic going on is like this first call to updateCell is made where viewModel is same as myChannelVM but channelCellModel is a channel with updated state.
Then second time call to updateCell is made where viewModel is same as myChannelVM but not having that channel which was earlier marked unfavorite.
Now if I remove collectionView.reloadItems(at: [indexPath]) from else block and use collectionView.reloadData() in place of it then collection view updates correctly otherwise not.
So updateCell(at:) is interfereing with reloadData() of when made in next call.
So there is issue like Why is a call to reloadItems(at:) stopping/breaking call to reloadData() in Collection View?
What is the reason for this in my case?

How to use applicationWillResignActive to call a function of a view controller

I am trying to call my menu view inside my view controller when the home button is pressed, or for that matter when the user gets a phone call, etc...
My goal is to call the function: toggleMenu() that is inside the QuestionViewController. Here's the code:
class QuestionViewController: UIViewController, MFMailComposeViewControllerDelegate {
///////////
func toggleMenu() {
// Show or hide menu
if menuStackView.isHidden == true {
// Show the menu
print("Showing Menu")
// Update labels
questionNumberMenuLabel.text = questionNumberLabel.text
endTimer()
menuStackView.isHidden = false
animateInMenu()
} else {
// Hide the menu
animateOutMenu()
}
}
I do believe I should utilize the following method found in the AppDelegate.swift file:
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
If I'm on the right track I need help calling the toggleMenu() function of the QuestionViewController class in this method. How do I do that?
Thanks so much!
Use the NotificationCenter and listen for UIApplicationWillResignActiveNotification. The system broadcasts that notification as well as calling your app delegate's applicationWillResignActive method (assuming you have one.)
Listen for notifications (using the method addObserver(forName:object:queue:using:) in your viewDidLoad. If you don't need to support iOS versions < 9.0, you don't need to worry about calling removeObserver - the system takes care of it for you.

Currently messing with Swift Reachability, should I do all my code in viewDidLoad or in viewDidAppear?

I'm currently trying to implement Reachability into my current project. I followed a tutorial on YouTube that worked but I'm unsure whether or not its the correct way of doing it. In the Reachability documentation (https://github.com/ashleymills/Reachability.swift) it shows two examples first one being 'Example - closures' where I assume it's done in the viewDidLoad?
//declare this property where it won't go out of scope relative to your listener
let reachability = Reachability()!
reachability.whenReachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
if reachability.isReachableViaWiFi() {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability.whenUnreachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
print("Not reachable")
}
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
and the last example was 'Example - notifications', this is where I get confused the creator says to do that all in viewDidAppear. Is there really a big difference if I just do everything inside viewDidLoad? Does it change the outcome of anything? It currently works fine but I'm not sure whether it's right, I don't want it affecting me in the future. Any help would be great! Thanks.
It depends on your needs.
If you want to use Reachability...
... dynamically only if this particular view is frontmost, startNotifier() in viewWillAppear and stopNotifier() in viewDidDisappear.
... in this particular view as long as the view is alive/loaded startNotifier() in viewDidLoad.
... globally in all views put the entire code in AppDelegate and post notifications.

WatchOS2 UI updates sent to page based WKInterfaceController when controller is not current page runs but does not update any UIs

I have an app with 3 pages, when I receive information from my WatchConnectivity delegate, which is my Extension Delegate, I delegate it to the InterfacController to update the UI.
However, the code that handles the update runs, including the UI update, but the UI is actually not updated.
Seems like you can only update UI of interfaceController when it is the current page.
My problem is, if the user goes to a different page when the data comes in, how can I tell when I return to that page, that the UI is updated or not. If I use a Bool to track the state, the Bool will be changed but not the UI.
The data takes time to load so I don't want to reload the data every time willActivate is called.
Not likes iOS UIKit, All the WKInterfaceObjects are not actual UI objects. They are just something likes remote controller for remote UI. (You know the bundle contains story board and the bundle contains extensions are separated, in sand-box concept, You can not access UI Objects directly.)
Connection between WKInterfaceObjects and actual UI Objects are established only the connected UI is active. Otherwise, any queries that are sent through WKInterfaceObjects are ignored.
Do likes below:
-(void) willActivate {
_active = YES;
[super willActivate];
if(_needsUpdate){
[self refresh];
}
}
-(void) willDeactivate {
_active = NO;
[super willDeactivate];
}
// make it to be called when the data changes
-(void) dataDidChange:(NSNotification)* note {
// If active, refresh UI, otherwise flag it.
// It will be handled in next activation.
if(_active)
[self refresh];
else
_needsUpdate = YES;
}
-(void) refresh {
__needsUpdate = NO;
// Update your UI here.
}
class InterfaceController: WKInterfaceController {
private var needsUpdate = false
private var data: AnyObject?
override func willActivate() {
super.willActivate()
updateDataIfExists()
updateViewIfNeeded()
}
private func updateDataIfExists() {
let update: AnyObject? = "" // get new data from ExtensionDelegate
if let update = update { // do not forget check if update != data
data = update
needsUpdate = true
}
}
private func updateViewIfNeeded() {
guard needsUpdate else { return }
// UPDATE UI
needsUpdate = false
}
}

Why does my UIButton delegate have to execute entirely before changes are shown in view?

I'm doing some MonoTouch development, and I really can't figure out an problem I've run into
I'm having an ViewController containing a UIButton. I have added a delegate to the TouchDown event of this button. In this delegate I'm calling a WebService and trying to change the colour and title of the button. However nothing happens to the button before the entire delegate have been executed. The thing is that the webservice is rather slow, so I want to give the users a waiting message by changing the colour and title of the button.
The code:
public override void ViewDidLoad ()
{
View.BackgroundColor = UIColor.Black;
bookButton = new UIButton( new RectangleF(10,100,this.View.Frame.Width-10 ,40) );
bookButton.BackgroundColor = UIColor.Clear;
setButton();
bookButton.TouchDown += delegate {
gymClass.book();
setButton();
tableView.ReloadData();
NavigationController.PopViewControllerAnimated( true );
};
this.View.AddSubview( bookButton );
}
Anyone, please?
The delegate is executed on the main thread which is responsible for rendering, so you are blocking the renderer until you return.