UISplitViewController Master Content Width - ios

I have a UISplitViewController in my application (MvvmCross / Xamarin iOS) and for some reason I cannot get the content to respect the dimensions of the available view areas.
In the situation shown in the screenshot the master view is hosting a UIViewController with a TableView inside. All the layouts are done with constraints and work fine on their own when running in an iPhone emulator.
As soon as I switching to running on an iPad some custom code I have in my presenter shows this same view in the master panel of a UISplitViewController but in this situation the constraints seem to be ignored and I end up with a view that looks like this:
As you can see the right hand side of the table cell is now way off the viewable area of the master panel of the UISplitViewController.
Both the UITableView and the UITableCell both use View.Frame as their initial size (I've tried View.Bounds as well).
How can I get the cells and / or table to respect the bounds of the UISplitViewController available space?

Thanks to Cheesebarons question I found my solution (cause).
I have a set of methods in a helper class that I use to generate my "default" UIViews.
One of these methods creates my default UITableView:
public static UITableView CreateDefaultTableView(CGRect rect, UITableViewStyle style)
{
var tv = new UITableView(rect, style)
{
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine,
SeparatorColor = IosConstants.DefaultTableSeparatorColor,
BackgroundColor = IosConstants.DefaultViewBackgroundColor
};
return tv;
}
Changing:
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
To:
AutoresizingMask = UIViewAutoresizing.All,
Schoolboy error!

Related

Xamarin iOS large tittle scroll velocity snapping issue

I am using Xamarin.Forms version 5.0 to develop an app for iOS 15. I am struggling to smooth the large title scroll transition. My problem is outlined on this question: iOS 11 large navigation bar title unexpected velocity
According to the above link, It seems like a fairly simple solution on Swift but I am struggling to do this using Xamarin.iOS.
Using a custom renderer, I have the following settings:
ExtendedLayoutIncludesOpaqueBars = True;
EdgesForExtendedLayout = UIRectEdge.Top;
AutomaticallyAdjustsScrollViewInsets = true;
NavigationController.NavigationBar.PrefersLargeTitles = true;
NavigationItem.LargeTitleDisplayMode = UINavigationItemLargeTitleDisplayMode.Automatic;
NavigationController.NavigationBar.Translucent = true;
I have tried modifying the constraints of my ScrollView by setting them to the constraints of the View. View.Superview is null for this page and would not allow me to change those constraints.
I have tried all sorts of different combinations of settings.
I have tried changing my ScrollView to a TableView.
I know I could make a custom navigation bar that could simulate this transition but I am trying to use native iOS settings and UI designs.
One of the answers on this question suggest the only way to get the smooth scroll transition is to use a UITableViewController instead of a UIViewController with a UITableView inside. Is this possible on Xamarin? I haven't found a way to use UITableViewController when designing the UI in Xaml. Even if I can change to a UITableViewController, will I be able to set the constraints correctly in the renderer?
Are there any other things I should try?
Thank you for any responses.
Try to find the ScrollView and change its constraints .
[assembly: ExportRenderer(typeof(ContentPage), typeof(MyBarRenderer))]
namespace FormsApp.iOS
{
internal class MyBarRenderer : PageRenderer
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
ExtendedLayoutIncludesOpaqueBars = true;
foreach (UIView view in View.Subviews)
{
if (view is UIScrollView sc)
{
NSLayoutConstraint.ActivateConstraints(new NSLayoutConstraint[] {
sc.TopAnchor.ConstraintEqualTo(View.TopAnchor),
sc.LeftAnchor.ConstraintEqualTo(View.LeftAnchor),
sc.BottomAnchor.ConstraintEqualTo(View.BottomAnchor),
sc.RightAnchor.ConstraintEqualTo(View.RightAnchor),
});
}
}
}
}
}
Refer to
iOS 11 large title navigation bar snaps instead of smooth transition

how to change view controller width size programatically?

I am trying to make a viewcontroller manually without using pod following this tutorial https://www.youtube.com/watch?v=Hl_Re_KLhcY&t=141s
but the sideMenuVC (the green one) has width that set accurately, I mean, the sideMenuVC should be 80% of the size of MainMenuVC. so if I change from iPhone 5s to iPhoneX, the autolayout still look perfect. But this is what I get
the sideMenuVC should be 80% only, but it looks full size on iPhone 5s
in sideMenuVC viewDidLoad I want to set something like this
class SideMenuVC: UIViewController {
#IBOutlet var sideMenu: UIView!
override func viewDidLoad() {
super.viewDidLoad()
sideMenu.frame.width = self.view.frame.width / 2
}
}
but it doesn't work since it is get only property
so I want to change the width in here
What you would optimally want to achieve your desired output is keep the view controller's width as is, but add a normal view inside with the following constraints
Leading to superview with a constant value of 0
Top to superview with a constant value of 0
Bottom to superview with a constant value of 0
Width equal to superview with a multiplier value of 4:5
You also need to set the background color of the UIViewController's default view property to .clear.
Finally, set the presentation style of that newly created UIViewController to Over Current Context.
When presenting a view controller using the UIModalPresentationFullScreen style, UIKit normally removes the views of the underlying view controller after the transition animations finish. You can prevent the removal of those views by specifying the UIModalPresentationOverCurrentContext style instead. You might use that style when the presented view controller has transparent areas that let underlying content show through.

Xamarin iOS Autolayout: Resize width and vertical scroll automatically for various devices while keeping the horizontal scroll disabled

I want to create a page which has a vertical but no horizontal scroll. It must adjust width of the content and vertical scroll automatically as per screen size.
Something similar to this:
I can not use UITableView since, my page may not have necessarily homogenous elements. It could have a combination of textfields , dropdown etc.
The previous answer was quite right, but not right at all. Indeed I tried to solve this problem using the method described before, but to make it work, I made some adjustments.
Your view's hierarchy has to be as follow :
UIScrollview :
View1
View2
View3
You don't need a container inside the UIScrollview, because apart the fact that it will be an extraview that you don't need, there is the problem that if you use this container-view you will get problem getting touch events in the views added.
So, let's make a step-by-step process:
Add scrollview to your viewController
The first step is to add the scrollview to your viewController, and we can simply do this programmatically in the following way:
UIScrollView scrollView = new UIScrollView();
scrollView.TranslatesAutoresizingMaskIntoConstraints = false;
View.AddSubview(scrollView);
View is the main-view of the viewController you are working in (aka Self.View).
Put attention to set TranslateAutoResizionMaskIntoConstrains property of the scrollview to false, otherwise autoresizing will mess your constraints.
Add constraint (autolayout) to your scrollView
You need to ensure that you layout will adjust for every different iPhone-screen, so simply use auotlayout to pin your scrollView to the viewController main-view (is the View used in the next code sample):
scrollView.TopAnchor.ConstraintEqualTo(View.TopAnchor, 0).Active = true;
scrollView.BottomAnchor.ConstraintEqualTo(View.BottomAnchor, 0).Active = true;
scrollView.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 0).Active = true;
scrollView.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, 0).Active = true;
In this way your scrollView is pinned to the bound of the main-view.
Create the view to be added
You need to create the view that you will add to the scrollView:
UIView viewToBeAdded = new UIView();
viewToBeAdded.TranslatesAutoresizingMaskIntoConstraints = false;
viewToBeAdded.Frame = new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, 200);
We have created a new UIView that setting its frame large as the screen (UIScreen.MainScreen.Bounds.Width) so it won't scroll horizontally, and with an arbitrary height (200 in the sample).
NOTE : even in this case you have to set TranslateAutoResizingMaskProperty to false, otherwise you will get a mess.
Add the view to the scrollView
Next step is to add our new view to the scrollView as follow:
scrollView.AddSubview(view);
Nothing more.
Set constraint for the view added in relation to the scrollView
Once you have added your view you have to said which will her behavior related to the scrollView. We assume that we will add several view to the scrollView, so we have to made a distinction, to the behavior of the FIRST view, the IN-BETWEEN views, and the LAST view.
So to be clear we assume that we are adding only 3 views, so we will have the three different cases.
FIRST VIEW
The important thing is that the first view has to be pinned to the top of the scrollView, we do this as follow :
firstView.TopAnchor.ConstraintEqualTo(scrollView.TopAnchor, 0).Active = true;
and then we set the others constraints:
firstView.WidthAnchor.ConstraintEqualTo(firstView.Bounds.Width).Active = true;
firstView.HeightAnchor.ConstraintEqualTo(firstView.Bounds.Height).Active = true;
IN-BETWEEN VIEW
The in between views (in our sample the secondView) need to be pinned to the previous view added (in our case the first view). So we do as follow:
secondView.TopAnchor.ConstraintEqualTo(firstView.BottomAnchor).Active = true;
So the top of the secondView is pinned to the bottom of the firstView.
And then we add the others constraints:
secondView.WidthAnchor.ConstraintEqualTo(secondView.Bounds.Width).Active = true;
secondView.HeightAnchor.ConstraintEqualTo(secondView.Bounds.Height).Active = true;
LAST VIEW
The last view (in our case the third view) instead needs to be pinned to the bottom of the previousView (in our case the secondView) and to the bottom of the scrollView.
thirdView.TopAnchor.ConstraintEqualTo(secondView.BottomAnchor).Active = true;
thirdView.BottomAnchor.ConstraintEqualTo(scrollView.BottomAnchor).Active = true;
And the usual other constraints for width and eight:
thirdView.WidthAnchor.ConstraintEqualTo(thirdView.Bounds.Width).Active = true;
thirdView.HeightAnchor.ConstraintEqualTo(thirdView.Bounds.Height).Active = true;
In this way the eight of the scrollView will adapt to the eight of the views added, due to the fact that the views inside are pinned to the top and the bottom of the scrollView.
CONCLUSIONS
If you follow these simple instruction you will get everything work. Remember to disable autoResizingMask, as this is on of the common mistake.
Hope it was helpful.
Cheers
In a custom renderer for Xamarin.Forms i've written my UITableViewController like this:
_controller = new InfoFieldItemsTableViewController();
_controller.TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
_controller.TableView.SeparatorColor = UIColor.Clear;
_controller.TableView.AllowsSelection = false;
// http://useyourloaf.com/blog/self-sizing-table-view-cells/
_controller.TableView.RowHeight = UITableView.AutomaticDimension;
In my controller i am doing this to register all potential cell candidates:
private void RegisterCells()
{
foreach (var tuple in InfoFieldCellMapping.Map)
{
this.TableView.RegisterNibForCellReuse(tuple.Value.Item1, tuple.Value.Item2);
}
}
public override void ViewDidLoad()
{
RegisterCells();
base.ViewDidLoad();
}
I am doing this in my controller so cells resize themselves depending on how much height they need:
public override nfloat EstimatedHeight(UITableView tableView, NSIndexPath indexPath)
{
return 100;
}
Now all you need to do is create cell files from within your IDE which should be .xib files and design them in the editor using autolayout (so they can adapt to orientation changes automatically).
Within your TableViews datasource all that's left to do is mapping between your data item and it's corresponding cell similar to:
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var dataItem = Items[indexPath.Row];
var key = ""; // use info from data item to figure out which key identifies your table cell to dequeue the correct kind of cell
var cell = collectionView.DequeueReusableCell(key, indexPath) as UICollectionViewCell;
}
That's all you need really. In my scenario i am mapping fields which may contain different controls for date entries, number entries, long texts, short texts etc etc.
I hope that helps
1.Add Leading,Trailing,Top,Bottom Constraints on scrollView to it'superview.
2.Add UIView as containerView of scrollview and add 6 Constraints from containerView to scrollview as below.
a)Leading b)trailing c)top d)bottom e)Center Horizontally.
3.Make sure top elements in container view must bind to top by adding top constraints and also bind bottom most element to bottom of container view by adding bottom constraints.And also all the items between topmost and bottommost in the container view must be vertically connected to each other so it will define the content size of container view.
it will define the actual content height for scrollview.
and finally define content size for scrollview in code.
As I mentioned here .
Refer to Step 1 and Step 2 ,set constraints on Scrollview and containerView.
I remove the margin between Scrollview and View , and I add some controls on the containerView , so it looks like as below:
Notice
Since we set the containerView's width equal to scrollview's width, the width is fixed, so we can scroll vertically not horizontally.
Height of controls and spaces between them should be set clearly, because the contentSize is auto calculated by adding them. (If contentSize is greater than the height of screen ,the scrollview can be scrolled)
I saw you set those constrains on controls , but you can't scroll down to see the controls out of screen, I think you are missing to set bottom margin on the last control(the downmost one).
Let us do a test.
1. We set the margin (between button and textfield ) to 1000 and don't set bottom margin between the textfield and containerView.
Result : can't scroll down to see the textfield out of screen.
2. Set the margin 1000 and add a bottom margin(10) between textfiled and containerView.
Result: can scroll
Demo Link

Custom title view as large title in iOS 11 new navigation bar

I am using a button as a title view for my UITableViewController which opens a dropdown list of categories. Selecting a category filters content of the table view by the selected category.
The button shows the name of the selected category plus a small arrow, similar to how iBooks used to look (or maybe still looks? I haven't used it in a while). I would therefore like it to have the same behaviour as a standard title and have it be large at first and collapse when the table view is scrolled.
Is there a way to do this?
Thanks
It seems because of the new large titles, IOS11 requires the constraints on the custom view in the navigationItem.titleView to be set.
Do this for example:
customView.widthAnchor.constraint(equalToConstant: 200).isActive = true
customView.heightAnchor.constraint(equalToConstant: 44).isActive = true
self.navigationItem.titleView = customView
Note this must be done for both width and height.
It should work. No need to add a button, at least in my case...
This was suggested by Apple to ensure that you don't have zero-size custom views. See slide 33 in https://developer.apple.com/videos/play/wwdc2017/204/
Looks like touches are broken for navigationItem.titleView. Gestures, tap events and buttons - nothing works
Seems like a bug in iOS 11: https://forums.developer.apple.com/thread/82466
I provisionally implemented this workaround:
private lazy var navBarActionButtonIOS11: UIButton = {
button.addTarget(self.navTitleView, action: #selector(self.navTitleView.didTapView), for: .touchUpInside)
return button
}()
[...]
navigationItem.titleView = navTitleView
if #available(iOS 11.0, *), let navBar = navigationController?.navigationBar {
navBarActionButtonIOS11.removeFromSuperview()
navBar.addSubview(navBarActionButtonIOS11)
navBarActionButtonIOS11.center.x = navBar.center.x
}
Another solution could be to just assign a UIButton to navigationItem.titleView directly.
I hope Apple fixes this soon!
Well, I had same problem. I have UIButtons in UINavigationItem.titleView and those were not reacting to touches at all. Problem is that the view where those buttons are where of size (0,0) because of auto layout. So to fix this problem you need to add additional view into your custom view, lets call it "contentView" and put all your controls inside that contentView. Also, contentView must have defined size with constraints. Quick test is to add width and height constraint to contentView. And all works again.
Hope that this helps someone.

How to swipe to view maintaining navigation bar items?

I'm trying to implement the swipe to view functionality just like Twitter's Moments:
If you take a look at the top you'll see a horizontal slider with the views. My requirement is tad bit different... On top of that horizontal slider I need my regular navigation bar with each page's custom items.
The swipe to view I implemented with some minor errors. Please take a look at this video (this is what I've done so far):
http://sendvid.com/5j50p73z
As you can see the swipe views functionality is working but I'm still missing some important things, namely:
When swiping to the next view it is scrolling up (seen on video)
My views are all contained in navigation controllers with their respective navbar items. The way I see it I'd need something like PushViewController without the back button but that would work both directions... I'm kinda lost here If I add the navigation controller instead of the view it shows the bar, but below the current existing one, not substituting it.
Any ideas on how to solve these?
I'm developing using Xamarin.iOS, but if you can provide an example using swift I guess I could try to translate that :)
This is what I have so far:
var viewController = Storyboard.InstantiateViewController("detailClaimViewController") as DetailClaimViewController;
viewController.ClaimId = ClaimId;
AddChildViewController(viewController);
ScrollView.AddSubview(viewController.View);
viewController.DidMoveToParentViewController(this);
var sumsInsController = Storyboard.InstantiateViewController("sumsInsuredListViewController")
as SumsInsuredListViewController;
var sumsInsuredFrame = sumsInsController.View.Frame;
sumsInsuredFrame.X = View.Frame.Size.Width;
sumsInsController.View.Frame = sumsInsuredFrame;
sumsInsController.ClaimId = ClaimId;
sumsInsController.Title = $"Sums insured for {Title}";
AddChildViewController(sumsInsController);
ScrollView.AddSubview(sumsInsController.View);
sumsInsController.DidMoveToParentViewController(this);
ScrollView.ContentSize = new CGSize(View.Frame.Size.Width * 2, View.Frame.Size.Height - 49);
This is the code I "need" to work, because it loads the navigation bar and that ViewController's custom navbar items (as I said this one "works" but it shows the correct navigation bar below the standard one, it doesn't substitute it):
var viewController = Storyboard.InstantiateViewController("detailClaimViewController") as DetailClaimViewController;
viewController.ClaimId = ClaimId;
AddChildViewController(viewController);
ScrollView.AddSubview(viewController.View);
viewController.DidMoveToParentViewController(this);
var navController = new UINavigationController();
var sumsInsController = Storyboard.InstantiateViewController("sumsInsuredListViewController")
as SumsInsuredListViewController;
var sumsInsuredFrame = sumsInsController.View.Frame;
sumsInsuredFrame.X = View.Frame.Size.Width;
sumsInsController.View.Frame = sumsInsuredFrame;
sumsInsController.ClaimId = ClaimId;
sumsInsController.Title = $"Sums insured for {Title}";
navController.AddChildViewController(sumsInsController);
AddChildViewController(navController);
ScrollView.AddSubview(navController.View);
navController.DidMoveToParentViewController(this);
ScrollView.ContentSize = new CGSize(View.Frame.Size.Width * 2, View.Frame.Size.Height - 49);
Thank you!
Navigation items are defined per contained view controller within a UINavigationController. You can work around that in different ways, but my recommendation would be to use a custom UIViewController that acts as the container for the other UIViewControllers you want to present.
For example, you could build a custom UIViewController that contains your custom navigation bar and a view for the content itself, to act as a custom UINavigationController. With custom transitions, you can mimic the behaviour of the original class.
Please refer to this document for a better explanation on container view controllers.

Resources