I have the following setup:
my controller inherits UINavigationController.
It holds a member of DialogViewController
My controller's ContentSizeForViewInPopover is set to 400|400
The DialogViewController's ContentSizeForViewInPopover is also set to 400|400
One of the elements in the dialog view controller discloses a long list of sub elements. If this list is longer than the 400 units I defined, the popover controller's height will be increased if this list is pushed on the navigation controller. This is fine, however if I go back to the previous menu, the size won't be resized to the 400 units I defined.
Is there a way to tell DialogViewController to not change the size?
Can you show code for your specific usage of MonoTouch.Dialog? You may be seeing this behavior because every time the TopViewController changes...UIPopoverController will attempt to auto-negotiate it's own ContentSize.
You should set the ContentSizeForViewInPopover for each UIViewController being presented by overriding the WillShowViewController method and setting the SizeF there.
public class NavDelegate : UINavigationControllerDelegate
{
public NavDelegate()
: base()
{
}
public override void WillShowViewController(UINavigationController navigationController, UIViewController viewController, bool animated)
{
base.WillShowViewController(navigationController, viewController, animated);
viewController.ContentSizeForViewInPopover = new SizeF(.., ..);
}
}
Related
I am using MvvmCross and using ShowViewModel to navigate between view. It takes the default navigation behavior i.e., slides from Right->Left . I want few view controllers to slide from bottom to top . Can someone let me know how we can do this in MvvmCross. These are not overlays but regular view controllers.
You can achieve this by setting a custom delegate for your Navigation Controller. This can be done by override following method in your custom ViewPresenter
protected override void OnMasterNavigationControllerCreated()
{
this.MasterNavigationController.WeakDelegate = new NavigationControllerDelegate();
}
Within this delegate you can set your transitions, e.g.:
public class NavigationControllerDelegate : UINavigationControllerDelegate
{
public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForOperation(UINavigationController navigationController, UINavigationControllerOperation operation, UIViewController fromViewController, UIViewController toViewController)
{
if (operation == UINavigationControllerOperation.Push)
{
if (fromViewController is MenuViewController)
{
return new BottomToTopTransition();
}
...
}
}
}
BottomToTopTransition is also a custom class and inherits from UIViewControllerAnimatedTransitioning. Last step is to override AnimateTransition() in this transition class and you are done.
I have a storyboard set up like so:
As you can see, I have a home screen. It has access to a navigation controller. It has two ContainerViews, one used as a sidepane, the other as the main content. I plan to swap things into this main content container as I need them.
One of the things I would like to show in this container, when it is selected from the sidepane, is a CollectionView of cells that show people. Each cell has a photo and the person's details.
The best code example I could find for CollectionView was the Xamarin StateRestoration sample project. It includes a storyboard. I have followed the recipe as best I can while working around my own specific storyboard.
The problem now is that the GetCell(UICollectionView collectionView, NSIndexPath indexPath) method used to populate each cell in the CollectionView is not being called. As far as I know I'm supposed to instantiate the CollectionViewController's Datasource property. I've tried this in AppDelegate and in the ViewDidLoad of my CollectionViewController itself, and GetCell is still not getting called. Why is this?
Any help is appreciated! Ask me to edit if you need more information.
You need to set both datasource and the delegate. As you've mentioned you've set the datasource. To set the delegate open up the storyboard in xcode, select UICollectionView and control drag to the its parent view controller. Have a look at this gif.
Also make sure you implement IUICollectionViewSource interface in your target view controller
public partial class DetailViewController : UIViewController, IUICollectionViewSource
{
protected DetailViewController(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public nint GetItemsCount(UICollectionView collectionView, nint section)
{
return 10;
}
public UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.DequeueReusableCell("PersonCell", indexPath) as UICollectionViewCell;
return cell;
}
}
EDIT 1:
You can also do this programmatically. Enter the storyboard, in Widget properties set the name for the UICollectionView - PersonCollection. Then in code behind view controller override ViewDidLoad and set WeakDelegate and WeakDataSource (weak because we definitely not want to create cyclic reference and memory leaks for iOS)
public override void ViewDidLoad()
{
base.ViewDidLoad();
PersonCollection.WeakDataSource = this;
PersonCollection.WeakDelegate = this;
}
Hope this helps!
Here's a failing test:
#RunWith(RobolectricTestRunner.class)
public class FailTest {
#Test
public void heightAndWidth_shouldNotBeZero() {
TestActivity testActivity = Robolectric.buildActivity(TestActivity.class).create().resume().visible().get();
View contentView = testActivity.findViewById(69);
Assertions.assertThat(contentView.getWidth()).isNotZero();
Assertions.assertThat(contentView.getHeight()).isNotZero();
}
private static class TestActivity extends Activity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout contentView = new LinearLayout(this);
contentView.setId(69);
contentView.setLayoutParams(new LayoutParams(666, 666));
setContentView(contentView);
}
}
}
As you can see, I'm calling the visible() method on the ActivityController and driving the Activity lifecycle the correct way. Quoting the documentation:
Wait, What's This visible() Nonsense?
Turns out that in a real Android app, the view hierarchy of an
Activity is not attached to the Window until sometime after onCreate()
is called. Until this happens, the Activity's views do not report as
visible. This means you can't click on them (amongst other unexpected
behavior). The Activity's hierarchy is attached to the Window on a
device or emulator after onPostResume() on the Activity. Rather than
make assumptions about when the visibility should be updated,
Robolectric puts the power in the developer's hands when writing
tests.
So when do you call it? Whenever you're interacting with the views
inside the Activity. Methods like Robolectric.clickOn() require that
the view is visible and properly attached in order to function. You
should call visible() after create().
It seems as though I'm doing all I need to do. So why am I getting no height/width?
There is no layout pass in Robolectric, hence the view dimensions are always zero.
https://github.com/robolectric/robolectric/issues/819
I am new to Monotouch and attempting to understand how some of the basics hang together. Hopefully someone out there will be able to assist.
I've created a test project in MonoDevelop based on the Multi-Screened Apps tutorial on the Xamarin site and have extended it to include a tableView. I am having issues with referencing the Navigation Controller in a view that I need to push a detail view onto to display the detail of an item tapped in the table via an accessory button. I know some of the coding is scrappy, just been trying to get it working at this stage rather than the clarity in the code! (I'm using the latest versions of all Mono tools/libraries etc and XCode 4 on Lion). Starting at the beginning here's the code in FinishedLaunching in AppDelegate.
window = new UIWindow (UIScreen.MainScreen.Bounds);
this.rootNavigationController = new UINavigationController();
// Create a new homescreen
HomeScreen homeScreen = new HomeScreen();
// Add the homescreen to the root navigation controller
this.rootNavigationController.PushViewController(homeScreen, false);
// Set the root view controller on the window - the navigation controller
// will handle the rest.
this.window.RootViewController = this.rootNavigationController;
// make the window visible
this.window.MakeKeyAndVisible ();
homeScreen just contains a button which loads a new view containing a table view (OrderList). Here's the button event handler.
void HandleOrdersButtonhandleTouchUpInside (object sender, EventArgs e)
{
if (orderListScreen == null)
orderListScreen = new OrderList();
NavigationController.PushViewController(orderListScreen, true);
}
This all works fine. I've got some dummy data that loads into the table view, which also works fine. OrderData is a simple class for testing which just contains a couple of properties. I've added an AccessoryButton to the cells and am trying to load a detail view when this is tapped. Here's the code that does this - comment in code where issue is! (I'd previously tested the AccessoryButtonTapped functionilty was working by just displaying an alert).
public override void AccessoryButtonTapped (UITableView tableView, NSIndexPath indexPath)
{
var dataSource = (OrdersTableViewDataSource)tableView.DataSource;
if (detailScreen == null)
detailScreen = new OrderDetailScreen();
OrderData theOrder = dataSource.OrdersData[indexPath.Row];
detailScreen.currentOrder = theOrder;
// Cant get a reference to NavigationController here to push the detail view!
// this.NavigationController is not available
this.NavigationController.PushViewController(detailScreen, true);
}
My understanding of NavigationControllers from what I've read so far is that this reference should be available through all views that originate from the root ViewController/NavigationController without the need to pass the reference from AppDelegate through the various view constructors?
Can anyone tell me what I might be missing here?
Thanks in advance.
** An update after reviewing Jason's comment: (Please let me know if this is the incorrect way to post updates)
So, I tried the following:
I saved a reference to the NavigationController in the constructor for the ViewController that contains the table view as follows:
public partial class OrderList : UIViewController
{
UINavigationController navController;
public OrderList () : base ("OrderList", null)
{
this.Title = "Orders";
navController = this.NavigationController;
}
Then passed that into the TableViewDelegate, where the AccessoryButtonTapped is handled, in the ViewDidLoad method.
public override void ViewDidLoad ()
{
orderTableView.DataSource = new OrdersTableViewDataSource();
orderTableView.Delegate = new OrdersTableViewDelegate(navController);
base.ViewDidLoad ();
}
Then referenced that in the TableViewDelegate:
public class OrdersTableViewDelegate : UITableViewDelegate
{
UINavigationController navController;
public OrdersTableViewDelegate(UINavigationController controller)
{
navController = controller;
}
// Rest of class definition
}
Then the reference to the NavigationController using navController compiles with the code as previously described using the following in the AccessoryButtonTapped method:
navController.PushViewController(detailScreen, true);
When I run this and tap on the AccessoryButton I get a null reference exception on navController. The reference to this.NavigationController in the ViewController constructor is null. Am I doing something in the wrong place or sequence?
Cheers
The NavigationController property is on your table's view controller. If you are trying to reference it from your table's datasource, you need to pass a reference to the controller when you create the datasource.
I have a Custom element class that only resizes the rows when I rotate the device. On initial display the rows are not resized.
public class MultiImageElement : Element, IElementSizing
Which implements the IElementSizing interface:
#region IElementSizing implementation
public float GetHeight (UITableView tableView, NSIndexPath indexPath)
{
return 78;
}
#endregion
However, this is never called and the row heights remain the default size.
I add elements to my root in a loop:
for (int i = 0; i < secFolder.Rows; i++)
{
sec.Add (new MultiImageElement (secFolder.ThumbnailPathsForRow (i), imageSize));
}
this.Root.Add (sec);
I found the solution.
To have Monotouch.Dialog implement the IElementSizing, the Sections and Elements must be added to a local "myRoot" variable.
After it's been built out, then set the Dialog's Root to that.
var myRoot = new Root();
MakeElements()
this.Root = myRoot;
This works.
Another solution is custom DialogViewController and then setup Root.UevenRows to true:
public class CustomViewController : DialogViewController
{
public CustomViewController (RootElement root) : base(root)
{
Root.UnevenRows = true;
...
}
...
}
Might not be related, but happened to me that the initial rotate device event was not fired to me. So I implemented the flag and initiated a scheduled selector to be called upon app start to ensure the ShouldAutorotate... was called and performed the Layout for me if needed.