Hide MasterViewController in Master-Detail Application using Storyboard (XCode 4.2) - master-detail

I have created new application using Master-Detail Templte with storyboard. I want to give user facility where they can Hide/Show Master View Controller in Landscape mode. I found few examples on net but none are using story board and Master-Detail template with Navigation Controller.
I have already implemented splitviewcontroller willHideViewController & willShowViewController which help me hide Master View Controller in Portrait mode. I am using below code in didFinishLaunchingWithOptions method of App Delegate to load views intially,
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
I would appriciate if you can point me right direction.
Thanks,
Tapan Desai

The master view is presented in a popover and the popover can be used to control the visibility of the master.
So...follow these steps:
1) Create a property to hold the popoverController
#property (nonatomic, strong) UIPopoverController *pc;
2) Capture the popoverController on the SplitViewController willHide delegate call
-(void) splitViewController:(UISplitViewController *)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)pc
{
barButtonItem.title = #"Menu";
id detailViewController = [self.splitViewController.viewControllers lastObject];
[detailViewController setSplitViewBarButtonItem:barButtonItem];
self.pc = pc; //poppver controller
}
3) Finally just use the pc var to dismiss the popover
if (self.pc) {
[self.pc dismissPopoverAnimated:YES];
}

Related

Create View from ViewModel object

I see that MvvmCross touch supports creating a View from a ViewModel object using a MvxViewModelRequest.
But in MvvmCross WPF, I can only create Views from a MvxViewModelRequest using
Mvx.Resolve<IMvxSimpleWpfViewLoader>().CreateView(viewmodelRequest)
However, I cannot find a way to create a View from a ViewModel object? Is this support in MvvmCross for WPF?
This functionality isn't included by default in Wpf - but you could easily add it.
The logic would be similar to the request-based code in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Wpf/Views/MvxWpfViewsContainer.cs - something like:
// Use `IMvxViewFinder` to find the type of view:
var viewType = Mvx.Resolve<IMvxViewFinder>().GetViewType(myViewModel.GetType());
// create a view and set the data context
var viewObject = Activator.CreateInstance(viewType);
if (viewObject == null)
throw new MvxException("View not loaded for " + viewType);
var wpfView = viewObject as IMvxWpfView;
if (wpfView == null)
throw new MvxException("Loaded View does not have IMvxWpfView interface " + viewType);
wpfView.ViewModel = myViewModel;
You could build this into a custom views container or a custom view presenter if you wanted to.
Assume u have
public partial class LoginViewController : MvxViewController<LoginViewModel>
than, if i want to use view somewhere u can do something like
this.presentedCurrentController = Activator.CreateInstance(typeof(LoginViewController)) as LoginViewController;
(this.presentedCurrentController as LoginViewController).ViewModel = new LoginViewModel();
where
this.presentedCurrentController it's
var NSViewController presentedCurrentController;
Thanks too #cheesebaron for link and another one

iOS6 Orientation with NavigationController

I am developing an application that has several views and a root view controller. It's based on UINavigationController using pushViewController:
In didFinishLaunchingWithOptions:
BIDLoginController *loginController = [[BIDLoginController alloc] initWithNibName:#"Login" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:loginController];
[self.window setRootViewController:self.navController];
In loginController:
BIDMainPageController *mainPageController = [[BIDMainPageController alloc] initWithNibName:#"MainPage" bundle:nil];
[self.navigationController pushViewController:mainPageController animated:YES];
but the view controllers are subclasses of UIViewController, not UINavigationController. Now I want some of the views to support orientation(portrait and landscape) and some of them only support portrait. I have tried the following:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
It doesn't work. shouldAutorotate: method never been called. I have tried UINavigationControllerDelegate with no result either. When I change these view controllers' super class from UIViewController to UINavigationController, the view doesn't show up, only a navigation bar is shown. Can anyone help with this?
You can't do what you are describing. iOS 6 doesn't support forcing different child view controllers of a UINavigationController to rotate. They can respond differently to device rotation, but they cannot be forced to rotate.

Subclassing UICollectionReusableView

I've been trying to subclass UICollectionReusableView in a non-storyboard iPad project. I've built a view in IB and hooked it up to my custom class, registered the class for reuse in the viewController where my collection view lives, and am calling it correctly in
UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
However, nothing shows up in my header areas in the UICollectionView. I think I need too init the view with coder, but am unsure how to do that correctly. I followed a couple of examples I found, but the header view still does not appear in my collection view.
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[[NSBundle mainBundle] loadNibNamed:#"CVHeaderView" owner:self options:nil];
[self addSubview:self.categoryNameLabel];
}
return self;
}
Can anyone point me in the right direction?
If you use a storyboard and select the header/footer checkmark initWithCoder: will be called.
If you do not use the storyboard (or do not click header/footer) but hook it up manually you have to register your custom classes and initWithFrame: will be called.
[self.collectionView registerClass:[GameCardCell class] forCellWithReuseIdentifier:#"GameCardCell"];
[self.collectionView registerClass:[PlayerHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"PlayerHeaderView"];
[self.collectionView registerClass:[PlayerFooterView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:#"PlayerFooterView"];
Note: Both will be called only once. If the view comes out of the cache prepareForReuse will be called.
In my case, initWithFrame: is called automatically when dequeueing it for the first time. Try to implement this method and see if it works.

WPF / Prism library and multiple shells

I'm pretty new with Prism and after playing a bit around, there a few questions that arise. I'm trying to create a modular application that basically contains a map control in a shell window. The plugin modules offer different tools for interacting with the map. Some of the modules are pretty independent and simply display pins on the map.
1st question: How would RegionManager come into play for the module-specific classes (presenters) that must interact with the main map control? Usually in a RegionManager you register a specific view which is linked to a ViewModel, but in my case there is one single view (the map view) with multiple presenters acting on it.
2nd question: I need to be able to open several windows (shells) -- a bit like an MS Word document -- that should all be extended by the plugin modules. In a single-shell environment, when the module specific classes were instantiated, they could use the Dependency Injection Container to get a reference to the RegionManager or the Shell itself in order to get access to the map control. However with multiple shells, I don't see how to get access to the map control of the right shell. The dependency container has references to object global to the application, not specific for the shell I'm currently working in. Same is true for the EventAggregator.
Any input would be very welcome,
Ed
After hours of reading Prism-related articles and forums I've come across the article "How to build an outlook style application" on Erwin van der Valk's Blog - How to Build an Outlook Style Application.
In one part of the architecture, a Unity Child Container was used to resolve type instances. That's exactly what I needed for the answer to my 2nd question: I needed to have "scoped" (by window) dependency injection (ex: window scoped EventAggregator, Map control, etc.)
Here's how I create a new window:
private IShellWindow CreateNewShell(IRegionManager regionManager)
{
IUnityContainer childContainer = this.Container.CreateChildContainer();
... register types in child container ...
var window = new ShellWindow();
RegionManager.SetRegionManager(window, regionManager);
window.Content = childContainer.Resolve<MapDocumentView>();
return window;
}
So MapDocumentView and all its components will be injected (if needed) window-scoped instances.
Now that I can have scoped injected objects, I can get the window-scoped map in my module-based MapPresenter. To answer my 1st question, I defined an interface IHostApplication which is implemented by the Bootstrapper which has a MapPresenterRegistry property. This interface is added to the main container.
Upon initialization, the modules will register their presenters and upon the window creation, they will be instantiated.
So for the module initialization:
public void Initialize()
{
...
this.hostApplication.MapPresenterRegistry.Add(typeof(ModuleSpecificMapPresenter));
...
}
The code that initializes the map window:
private void View_Loaded(object sender, RoutedEventArgs e)
{
// Register map in the == scoped container ==
container.RegisterInstance<IMap>(this.View.Map);
// Create map presenters
var hostApplication = this.container.Resolve<IHostApplication>();
foreach (var mapPresenterType in hostApplication.MapPresenterRegistry)
{
var mapPresenter = this.container.Resolve(mapPresenterType) as IMapPresenter;
if (mapPresenter != null)
{
this.mapPresenters.Add(mapPresenter);
}
}
}
The module-specific MapPresenter:
public ModuleSpecificMapPresenter(IEventAggregator eventAggregator, IMap map)
{
this.eventAggregator = eventAggregator;
this.map = map;
this.eventAggregator.GetEvent<AWindowSpecificEvent>().Subscribe(this.WindowSpecificEventFired);
// Do stuff on with the map
}
So those are the big lines of my solution. What I don't really like is that I don't take advantage of region management this way. I pretty much have custom code to do the work.
If you have any further thoughts, I would be happy to hear them out.
Eduard
You have one main view and many child views, and child views can be added by different modules.
I'm not sure that the RegionManager class can be applied in this situation, so I would create a separate global class IPinsCollectionState
which must be registered as singleton in the bootstrapper.
public interface IPin
{
Point Coordinates { get; }
IPinView View { get; }
//You can use a view model or a data template instead of the view interface, but this example is the simplest
}
public interface IPinsCollectionState
{
ObservableCollection<IPin> Pins { get; }
}
Your main view model and different modules can receive this interface as a constructor parameter:
public class MapViewModel
{
public MapViewModel(IPinsCollectionState collectionState)
{
foreach (var item in collectionState.Pins)
{ /* Do something */ };
collectionState.Pins.CollectionChanged += (s, e) => {/* Handle added or removed items in the future */};
}
//...
}
Example of a module view model:
public class Module1ViewModel
{
public Module1ViewModel(IPinsCollectionState collectionState)
{
//somewhere in the code
collectionState.Pins.Add(new Module1Pin());
}
}
The second question can be solved in many different ways:
Application.Current.Windows
A global MainViewModel which contains the list of ShellViewModels and if you add new view model it will be displayed in new window. The bootstrapper is single for all windows.
Some kind of shared state which is passed to the constructor of the bootstrapper.
I don't know how these windows are related between themselves, and I don't know which way is the best, maybe it is possible to write an application with separated windows.

Prism v4, MEF, WPF - Using proper Uri for module graphic

My solution's structure is:
CI.Frontier.Classic contains a MEF module. My application uses the RibbonWindow control, and the modules define what menu items should be created. I can successfully add a button to the ribbon control from the CI.Frontier.Classic module, however, I cannot figure out the proper Uri to ClassicFrontierToopTip.png
Heres the code in FrontierClassic.cs that creates the tab, button and attempting to set the ribbon ToolTipImage
public void CreateMenuItems()
{
TabData tabData = new TabData("Legacy");
GroupData groupData = new GroupData("Internal");
tabData.GroupDataCollection.Add(groupData);
ButtonData classicFrontierBtn = new ButtonData()
{
Label = "Classic Frontier",
ToolTipTitle = "Classic Frontier",
ToolTipDescription = "Open Classic Frontier",
ToolTipImage = new Uri("./Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
};
classicFrontierBtn.Command.RegisterCommand(new DelegateCommand(LoadFrontierView));
groupData.ControlDataCollection.Add(classicFrontierBtn);
_ribbonService.AddTab(tabData);
}
This Uri doesn't work as the tooltip does not display. Can I use the UriKind.Relative or should I be using some sort of "pack uri"?
The robust approach would be to leverage the pack syntax...
new Uri("pack://application:,,,/CI.Frontier.Classic;component/Graphics/ClassicFrontierToolTip.png", UriKind.Absolute);
Include an icon for a Prism module solved it...
ToolTipImage = new Uri("/" + GetType().Assembly.ToString().Split(',')[0] + ";component/Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
Not sure if this is the best solution though.

Resources