I've been playing around with CoreData in the last couple days, trying to build an app to review money spent on shopping. Right now its still pretty simple with just a single Data Model for the individual shops.
I have a list view displaying all of them and I've integrated sorting into the list, first through older workarounds around the predicates, but than I found this video from this years wwdc and I basically just copied. I've really been fascinated by the grouping feature from the SectionedFetchRequest and I wanted to integrate it, while maintaining the original non sectioned List. So I thought I'd skip the FetchRequest in my List and just pass the results to the list view instead of the SortDescriptor
MainView{
ListView(descriptor: SortDescriptor)
}
ListView{
FetchRequest(sortDescriptors: descriptor)
}
changed to:
MainView{
ListView(FetchRequest(sortDescriptors: descriptor))
}
ListView{
FetchedResults
}
But that still leaves me unable to just push a button to turn sectioning on or off.
I'm kind of stuck on how to go on from here.. First idea coming to my mind is creating a wrapper around the ListView handeling which FetchRequest to send out to the ListView based on Button toggle state like
MainView{
Wrapper(sortDescriptors, toggleState)
}
Wrapper{
ListView(FetchRequest(sortDescriptors: descriptor))
}
ListView{
FetchedResults
}
but I still would have the problem that I'd need 2 Variables in my ListView, one for the normal, and one for the sectionedFetchResults.
Has anyone an idea how to handle this ?
TLDR I want to dynamically switch between FetchRequest and SectionedFetchRequest
According to the documentation you can't not section a SectionedFetchRequest, therefore you would have to support both. Therefore, I would make two separate sub views, and show them in a parent view that has logic to control which one is shown. You would need to do this anyway if you are supporting pre-iOS 15 OS's.
MainView{
if sectioned
ListViewSectioned(sortDescriptors: descriptor, sectionID: sectionID)
} else {
ListView(sortDescriptors: descriptor)
}
}
ListView{
FetchRequest(sortDescriptors: descriptor)
}
ListViewSectioned{
SectionedFetchRequest(sectionIdentifier: sectionID, sortDescriptors: descriptor)
}
The main view doesn't have to know any more to choose and set up the different list views. I didn't put an OS check in, but you will need that as well.
Related
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.
I'm not necessarily looking for code help, but rather a high level answer so I can research the solution myself. Basically, I have an MDI app with multiple docs and their views, I'd like all the views to open up as tabs in the one child frame that I have. The thing is my child frame is statically configured with a splitter window with two views, a form and a list view, in the OnCreateClient method. I'd like to keep this as the default tab that appears when the app is launched.
I have a third view (editview) with it's own document template, which I'd like to be able to open as a separate tab. I will have other views that will behave this way. What's the best way to approach this?
Will I need to create separate child frames for each view? Will I lose the 'tab' feature if I create separate child frames?
Or will I have to modify the child frame's OnCreateClient method to test which document template is the current one and create the view for that doc template? I'd like to know how some of you seasoned programmers have had or would do it.
Thanks.
In case this helps others, from what I've gathered, it is perfectly acceptable to create a new child frame class derived from CChildFrame or just use that as your frame with your new view. The doc, frame, and view will be added to the doc template in the initInstance method. for example, let say you have a pair of trios (2 docs, 2 views, 2 frames):
pDocTemplate = new CMultiDocTemplate(IDR_testappTYPE,
RUNTIME_CLASS(CMydoc1),
RUNTIME_CLASS(CMyframe1),
RUNTIME_CLASS(CMyview1));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
pDocTemplate2 = new CMultiDocTemplate(IDR_testappTYPE,
RUNTIME_CLASS(CMydoc2),
RUNTIME_CLASS(CMyframe2),
RUNTIME_CLASS(CMyview2));
if (!pDocTemplate2)
return FALSE;
AddDocTemplate(pDocTemplate2);
If you add another trio with a different childframe because this new frame doesn't use splitters like the ones above, you would do it this way.
pDocTemplate3 = new CMultiDocTemplate(IDR_mditest3TYPE,
RUNTIME_CLASS(CMydoc), //same doc
RUNTIME_CLASS(CMyframeWithoutSplitters), //new frame
RUNTIME_CLASS(CMyview3)); //new view
if (!pDocTemplate3)
return FALSE;
AddDocTemplate(pDocTemplate3);
while using map view in my application some times MKMapKit delegate method 'mapView: regionDidChange' do not call.
Its happens only when I drag the map. but when i zoom in or Zoom out Its working perfectly. So its create issue related to place new annotations on map while dragging the map.
I have do this code in mapView:regionDidChange:
int j=0;
-(void) mapView:(MKMapView *)mapsView regionDidChangeAnimated:(BOOL)animated{
zoomLevel = self.mapView.region.span.latitudeDelta;
if (![appDelegate internetConnected]){
return;
}
if (appDelegate.isMapViewRegionChanged) {
if (j==0) {
j++;
return;
}else{
j=0;
appDelegate.isMapViewRegionChanged = FALSE;
return;
}
}
[self callGetMapViewWithObject:nil];
}
/*
first boolean is to check Internet connection.
[appDelegate internetConnected]
Second condition is to return when we navigate from any view controller too map View controller.
appDelegate.isMapViewRegionChanged
Third is a method to place new annotations.
[self callGetMapViewWithObject:nil];
*/
I checked all conditions and booleans but my coding is not reason for this bug.
so may be its related to region did change method.
So while using my app with map, 20% of time its behave like Ideal(method do not call).
can some one help me out with this.
Thank you in advance.
EDIT It broke randomly, so I now call the function again (undoing what I said below), no changes extra... and um, it works. I feel like I'm flipping a coin.
I just had this happen because I have a subclassed MKMapView. I don't know if you're subclassing this or not, but for some reason Apple's super functions, eg: -(void) scrollViewDidScroll; called super but was not intercepted properly and skipped that call.
When I removed the "overridden" call, that was just a call to [super scrollView], it started working properly.
I don't know why apple's code is broken that way (calling super doesn't have the same effect not overriding it), but make sure you're not subclassing these:
ScrollView functions
MKMapView functions...
or perhaps using the WildCard Gesture Recognizer provided very kindly by the answer to why Map Views don't respond to touchesBegan/Moved etc here: How to intercept touches events on a MKMapView or UIWebView objects? .
If this doesn't help, ensure you don't have a view on top of the other views, improper delegates, xibs are arranged and hooked up, the usual stuff.
I have a Core Data app with a tab-bar controller that displays 2 view controllers. If I add something in the first tab's view controller, it should display in the 2nd tab's VC. Both VCs are based off a NSFetchedResultsController which is based off the same entity; the only difference is that one has a predicate and the 2nd VC doesn't.
This works fine for the normal template, and when data is added from the 1st VC, it gets updated instantly in the 2nd tab using controllerWillChangeContent and controllerDidChangeContent. The problem is that if the user adds or deletes any rows in the 1st VC, when the user comes to the 2nd tab they don't see the rows animatedly inserted or deleted... everything's already there.
What I would like to do, in the 2nd tab's VC, is delay calling the [self.tableView endUpdates] (which causes the animated inserting/deleting of rows in the table) till the user actually goes to that tab, in that VC's viewWillAppear. I've tried this, but doesn't seem to work:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
tableviewUpdates = TRUE;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (tableviewUpdates) {
tableviewUpdates = FALSE;
[self.tableView endUpdates];
}
}
This works if adding one row at a time and then switching to the 2nd tab, but not if I add multiple rows in the 1st tab and then switch.
Any help would be appreciated.
You're working against the purpose of the NSFetchedResultsController which is to make updating the tableview automatic and effortless.
I'm pretty sure, however, that if you override all the FRC delegate methods you can block all the automatic updates.
You might want to rethink this design. Are users really going to expect to see changes in one view reenacted in a second? Will they understand they are watching a rewind of the previous changes or will they intuitively think that the app is doing something to their data on its own?
The standard UI grammar teaches users to expect that one change animates once and then subsequently just shows up in a standard display. I would suggest you test this design with naive users carefully before deploying such a non-standard interface.
I am trying to implement search option for my file browser application.
I can get the item after taking an input from the user. Now, I want to add this item to my listview. Also after the search is over, the item should be clickable to open them.
Here, is the piece of code... Any suggestions will be appreciated.
void Browser::search()
{
QDirIterator it(path,QDir::AllDirs|QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot,QDirIterator::Subdirectories);
while(it.hasNext())
{
it.next();
if(it.fileInfo().completeBaseName().contains(content,Qt::CaseInsensitive))
{
qDebug()<<"it.fileinfo = "<<it.fileInfo().fileName();
}
}
path.clear();
}
Instead of List View you can use List Widget and simply fill the list using method addItem or addItems. If your list is small/simple it doesn't in my opinion make sense to use Model-View paradigm.
Look at QListWidget in documentation
You should read more about model/view concept. You add data to your model not to a view. You'll find in this article sections "Editable items" and "Resizable models", which address your issue.