Enable Undo CoreData SwiftUI - core-data

Does anyone know how to have the Undo command in the menu bar enable when CoreData UndoManager has items to undo?
Here’s what I’ve done:
At app init I’m setting the Core Data PersistanceController UndoManager()
persistenceController.container.viewContext.undoManager = UndoManager()
I can confirm the CoreData undoManager is set because I can programmatically call undo and it works (for instance after deletion)
HOWEVER the Undo command in the menuBar remains disabled.
As a side-note, the Undo command in the menuBar works correctly when I’m writing text in a TextField (it does automatically enable to undo text changes).
Huge thanks in advance, been trying things for a while and can’t find anything online

Context is not clear, but try (just an idea) to use Undo Manager from environment, like
// ... somewhere in view where you change core data objects
#Environment(\.managedObjectContext) var viewContext
#Environment(\.undoManager) var undoManager
// ...
var body: some View {
// ...
.onAppear {
viewContext.undoManager = undoManager
}
}

Related

create or inject ViewModel when building a "tabs" application

We try to build an application with a few tabs. As reference-project we use that example: http://slodge.blogspot.co.uk/2013/06/n25-tabs-n1-days-of-mvvmcross.html
To get the ViewModel-instances we need to create the tabs, we used the "HomeViewModel"-pattern as mentioned in that post: Create View Model using MVVMCross built in factory?
What I don't like at this approach is the initialisation of ViewModel's with "new". As far as I understand, it skips the whole ViewModel-Lifecycle (https://github.com/slodge/MvvmCross/wiki/View-Model-Lifecycle) which we really like. In our current project, we'd like to use the "start()" lifecycle-method, but it's never called due to initialisation with "new".
What worked for us was to go that way:
var loaderService = Mvx.Resolve<IMvxViewModelLoader>();
var vm = (UserListViewModel)loaderService.LoadViewModel(new MvxViewModelRequest(typeof(UserListViewModel), null, null, null), null);
So my question: Is that the way to do the job or is it just a dirty workaround and there is a much better solution?
Update: We came to that solution:
CreateTabFor<SettingsViewModel>("Settings", "settings");
//This method loads the ViewModel
private UIViewController CreateTabFor<TTargetViewModel>(string title, string imageName)
where TTargetViewModel : class, IMvxViewModel
{
var controller = new UINavigationController();
controller.NavigationBar.TintColor = UIColor.Black;
var viewModelRequest = new MvxViewModelRequest(typeof(TTargetViewModel), null, null, null);
var screen = this.CreateViewControllerFor<TTargetViewModel>(viewModelRequest) as UIViewController;
SetTitleAndTabBarItem(screen, title, imageName);
controller.PushViewController(screen, false);
return controller;
}
The 'viewmodel lifecycle' is an area of conflicting interests in MvvmCross. The root cause is the conflict between:
viewmodel's which are just the models for any view
viewmodel's which are specifically used within the 'ShowViewModel' navigation process
For simple 'whole page' User Experiences, the C-I-R-S viewmodel lifecycle is easy to support and to ensure it gets consistently used.
However, as soon as the user experience starts to merge in tabs, flyouts, hamburger menus, dialogs, split views, etc then:
the developers sometimes want to control viewmodel lifecycles themselves
it's not as easy for the framework to ensure that view models are always created, activated and tombstoned/rehydrated consistently
Personally, I like your approach - of trying to ensure all viewmodels are independent and all constructed the same way - but MvvmCross doesn't force this approach on all developers.
Specifically for tabs, most of the existing examples do use the 'owned sub-viewmodel' pattern that you've identified.
However, it should be relatively easy to implement other mechanisms if you want to - just as you already have.
In particular, you can:
use the loaderService directly - getting hold of it via Mvx.Resolve<IMvxViewModelLoader>();
use ShowViewModel with a custom presenter to create both views and viewmodels - the beginnings of this is illustrated in that N=25 video but you could take it much further and actually add the tabs in response to ShowViewModel calls.
use alternative calls to create the child tabs and their viewmodels inside the Views - e.g. where the Touch sample currently calls
var screen = this.CreateViewControllerFor(viewModel) as UIViewController;
this could easily be replace with something like:
var screen = this.CreateViewControllerFor<ChildViewModel>() as UIViewController;;
(or one of the other overloads from MvxCanCreateIosViewExtensionMethods.cs)
One repo where I know some users have taken some of these ideas and played with them is the Sliding menu repo - I think they have chosen to use this.CreateViewControllerFor<TViewModel> to create their view models. This may or may not be the way you choose to go - but it might be of interest for you to experiment with.

MonoTouch: add a custon view XIB

I would like to add a custom view XIB to my UITable views header. Following this link How to add a custom view to a XIB file defined view in monotouch i created a XIB with a view. It has just two labels in it.
I then have a cs file. Called XIB as mention in the link
public override UIView GetViewForHeader(UITableView tableView, int sectionIndex) {
var myHeader = new UIView();
var views = NSBundle.MainBundle.LoadNib("CustomHeaderView", this, null);
CustomHeaderView loginView = Runtime.GetNSObject(views.ValueAt(0)) as CustomHeaderView;
loginView.UpdateWithData("test","test");
myHeader.AddSubview(loginView);
return myHeader;
}
When i run the app it is crashing, dont see any error.
Make sure that your .XIB is not using AutoLayout and you are using an older iOS. The AutoLayout is new for 6.x and will cause a crash on the 5.x based devices. I see this quite often.

MonoTouch.Dialog: Dismissing keyboard by touching anywhere in DialogViewController

NOTE: There are two similar SO questions (1) (2), but neither of them provides an answer.
TL;DR: How can one dismiss the keyboard in a MonoTouch.Dialog by letting the user touch any empty space in the view?
I'm writing an app using MonoTouch.Dialog and a UITabBarController. One of my tabs is "Settings"...
When the user starts typing, the keyboard obstructs the tabbar...
Using MonoTouch.Dialog, the only way to dismiss the keyboard is to go to the last field and press the "return" key. Considering the fact that the user cannot press any tab until the keyboard is gone, I would like a better way to do it. Namely, to dismiss if the user taps anywhere else on the screen.
Without MonoTouch.Dialog, it's a snap: simply override TouchesBegan and call EndEditing. But this doesn't work with MT.D. I've tried subclassing DialogViewController and overriding TouchesBegan there, but it doesn't work. I'm currently at a loss.
Or, I wonder, would I be better off ditching the tabbar so I can use a UINavigationController with a "Back" button on top, which won't be hidden by the keyboard?
I suggest you use a tap gesture recognizer that will not cause interference with the TableView event handlers:
var tap = new UITapGestureRecognizer ();
tap.AddTarget (() => dvc.View.EndEditing (true));
dvc.View.AddGestureRecognizer (tap);
tap.CancelsTouchesInView = false;
You missed my question about it also: Can the keyboard be dismissed by touching outside of the cell in MonoTouch.Dialog?
:-)
This is my #1 feature request for MonoTouch.Dialog.
To answer your question: No. It is not possible. I have searched and asked around and have not found any answers.
I assume because it is just a sectioned (grouped) table and if it wasn't sectioned, there wouldn't be any spot to click. However, that is just my speculation.
I wish that miguel or someone that works on monotouch would answer this and say if it is even possible. Possibly a future enhancement?
I figured out a workaround that satisfies me well enough, so I'm answering my own question.
// I already had this code to set up the dialog view controller.
var bc = new BindingContext (this, settings, "Settings");
var dvc = new DialogViewController (bc.Root, false);
// **** ADD THIS ****
dvc.TableView.DraggingStarted += (sender, e) => {
dvc.View.EndEditing (true);
};
This will dismiss the keyboard whenever the user drags the view a little bit. There's no touch event I could find associated with the tableview, so this is the next best thing. I'd welcome any other ideas. Cheers!
One workaround to use the dragging gesture instead of the tap as proposed (that do not interfere with the table view gestures) is to override MonoTouch.Dialog.DialogViewController.SizingSource (or MonoTouch.Dialog.DialogViewController.Source if you don't want uneven rows) and give it to the DialogViewController. I don't know if it is very clean or safe.
public class CustomTableViewSource : MonoTouch.Dialog.DialogViewController.SizingSource
{
public CustomTableViewSource(MonoTouch.Dialog.DialogViewController dvc) : base(dvc)
{
}
public override void DraggingStarted(UIScrollView scrollView)
{
base.DraggingStarted(scrollView);
if (scrollView != null)
{
scrollView.EndEditing(true);
}
}
}

Hide tabbar and navigationbar when view is tapped in monotouch

I need to be able to hide the navbar and tabbar when I tap on the view and show it again when tapped again. Is this possible in Monotouch?
Anything that is possible with the native platform is possible with MonoTouch.
There are dozens of ways of achieving this. Perhaps the simplest thing to do is to create your own UIViewController that will host only the information you want and calling:
var myNewController = new MyNewController ()
myNewController.View.TouchDown += delegate {
myNewController.DismissViewControllerAnimated (false);
};
PresentModalViewController (yourNewController, false);
Then your myNewController must contain some code to add the actual contents that you want to show in full screen.

How to perform transition between controllers when using monotouch and storyboard?

I have created storyboard using following instructions
http://docs.xamarin.com/ios/tutorials/Introduction_to_iOS_5#Storyboards
It works as expected.
Then I added new button to MonkeyController and new ViewController to storyboard. This is the code I tried to use in button's click event
partial void btnClicked (NSObject sender)
{
UINavigationController ctrl = this.ParentViewController;
var screen = new TableRowViewController();
ctrl.PushViewController(screen, true);
}
It opens TableRowViewController, but it's background is black and it does not show any modifications I made on it on storyboard.
Can you help me with this?
I'm no expert yet, but it has been my experience trying to get it to work properly that if one of the sides of the split-view is black, then there is a segue that is illegal.
Hope that helps...

Resources