Weird glitch of UISwitch related to the UIScrollView on iOS with C# - ios

I have ViewContoller, and it is view hierarchy is the following (Cirrious.FluentLayout Library, but It should be understandable):
_scrollView = new UIScrollView()
{
ShowsVerticalScrollIndicator = false
};
View.Add(_scrollView);
_contentView = new UIView();
_scrollView.Add(_contentView);
View.AddConstraints(
_scrollView.Below(VueNavBar),
_scrollView.AtLeftOf(View),
_scrollView.AtRightOf(View),
_scrollView.AtBottomOf(View)
);
_scrollView.AddConstraints(_contentView.SameFrameAs(_scrollView));
Then I add UISwitch in the following way:
var switch = new UISwitch();
_contentView.Add(switch);
After setting up constraints (I have tried many sets of constraints, many of them are definitely not ambiguous) iOS renders me the following:
It is really weird, but after toggling it changes to:
Maybe someone have any ideas why it happens?
P.S.: I have managed to broke switches on other pages by adding scroll view in the same way, however they have been broken in a differently (Other type of glitch).

I tried the code below and the switch displayed normally.
And there is no need for you to define the width and height of a UISwitch.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
_scrollView = new UIScrollView()
{
ShowsVerticalScrollIndicator = false,
BackgroundColor = UIColor.Blue
};
View.Add(_scrollView);
this.View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(
_scrollView.AtTopOf(View,20),
_scrollView.AtLeftOf(View,20),
_scrollView.AtRightOf(View, 20),
_scrollView.AtBottomOf(View, 20)
);
_contentView = new UIView();
_contentView.BackgroundColor = UIColor.Orange;
_scrollView.Add(_contentView);
_scrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
_scrollView.AddConstraints(
_contentView.WithSameTop(_scrollView),
_contentView.WithSameLeft(_scrollView),
_contentView.WithSameWidth(_scrollView),
_contentView.WithSameHeight(_scrollView)
);
var switch1 = new UISwitch();
_contentView.Add(switch1);
_contentView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
_contentView.AddConstraints(
switch1.AtTopOf(_contentView, 80),
switch1.AtLeftOf(_contentView, 20)
);
}

Related

Why isn't cell resizing when content is taller than the EstimatedSize (sizeHint)

I ported a table view implementation from Swift with a variable height cell. But in my Xamarin/ReactiveUI implementation, the cell does not resize when the content grows (i.e. when an optional label is displayed). Please note that the cell does resize to fit in the native Swift implementation.
Most answers online focus on the setting of two properties on UITableView: RowHeight and EstimatedHeight as well as using AutoLayout. My row height is set to UITableView.AutomaticDimension and the EstimatedHeight is set to 44f. I am using autolayout constraints which I will show below. I am using the BindTo extensions in ReactiveTableViewSourceExtensions.
I also tried setting the Text property of the optional label right away to test the theory that setting the property up front would provoke a resize.
Here are the pertinent lines of code:
In a ReactiveViewController<T> ctor:
this.WhenActivated(disposables =>
{
//// ...
// This method automatically wires up the cell reuse key to be the nameof my cell class which is what I want.
this.WhenAnyValue(view => view._cells)
.BindTo<IImportedFileViewModel, ImportedFileCell>(TableView, 44f)
.DisposeWith(disposables);
//// ...
});
Below in ViewDidLoad:
//// ...
TableView = new UITableView
{
RowHeight = UITableView.AutomaticDimension,
EstimatedRowHeight = 44f, // Setting or not setting this doesn't matter
SeparatorStyle = UITableViewCellSeparatorStyle.None,
AllowsSelection = false,
TranslatesAutoresizingMaskIntoConstraints = false,
};
//// ...
In the cell implementation:
ClipsToBounds = true;
PreservesSuperviewLayoutMargins = true;
ContentView.ClipsToBounds = true;
ContentView.PreservesSuperviewLayoutMargins = true;
var stackView = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Vertical,
Distribution = UIStackViewDistribution.Fill,
Alignment = UIStackViewAlignment.Fill,
Spacing = 4,
};
var fileStackView = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Horizontal,
Distribution = UIStackViewDistribution.Fill,
Alignment = UIStackViewAlignment.Top,
};
FilenameLabel = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false,
Font = UIFont.SystemFontOfSize(15f, UIFontWeight.Medium),
};
fileStackView.AddArrangedSubview(FilenameLabel);
StatusImage = new UIImageView()
{
TranslatesAutoresizingMaskIntoConstraints = false,
ContentMode = UIViewContentMode.Center,
};
fileStackView.AddArrangedSubview(StatusImage);
stackView.AddArrangedSubview(fileStackView);
var reasonStackView = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Horizontal,
Distribution = UIStackViewDistribution.Fill,
Alignment = UIStackViewAlignment.Top,
};
// This is the optional label that, when its Text property is set, should resize the cell.
FailureReasonLabel = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false,
Font = UIFont.SystemFontOfSize(13f, UIFontWeight.Medium),
Lines = 0,
};
reasonStackView.AddArrangedSubview(FailureReasonLabel);
stackView.AddArrangedSubview(reasonStackView);
ContentView.AddSubview(stackView);
stackView.BottomAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.BottomAnchor).Active = true;
stackView.TopAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.TopAnchor).Active = true;
stackView.LeadingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.LeadingAnchor).Active = true;
stackView.TrailingAnchor.ConstraintEqualTo(ContentView.LayoutMarginsGuide.TrailingAnchor).Active = true;
StatusImage.HeightAnchor.ConstraintEqualTo(16f).Active = true;
StatusImage.WidthAnchor.ConstraintEqualTo(16f).Active = true;
I would be happy to get a solution to the problem using the basic structure I have now but I would also accept an example of some other pattern that someone has used (based on ReactiveUI) to get this working. I would prefer that the solution not be based on old manual resizing patterns like in the pre-iOS 8 days or some sort of hack.
So two things had to be adjusted to fix my issue.
The sizeHint parameter in the ReactiveUI BindTo method does not behave like TableView.EstimatedRowHeight, which is what I was assuming. So I ended up setting it to the auto dimension constant like this:
.BindTo<IImportedFileViewModel, ImportedFileCell>(TableView, (float)UITableView.AutomaticDimension)
I was thinking that I could just update the properties of the view models within the IObservableCollection implementation that is passed in to the BindTo method but it wasn't until I started provoking changed events on the collection itself (the collection that the view is bound to) that the cells started resizing themselves. So since I am using DynamicData as part of ReactiveUI, that meant calling SourceCache.AddOrUpdate(updatedViewModel) whenever I knew that the FailureReasonLabel had been set.
I am also going to try and restore the ReactiveUI tag that was removed from this post because I believe it to be a relevant part of this question and its answer.

ChildViewController/View does not get focus with GMSMapView

Context: I want to achieve something similar with How can I mimic the bottom sheet from the Maps app? based on https://github.com/grendio/XBottomSheet/tree/master/XBottomSheet.Samples/XBottomSheet.Touch
Problem: If I use the BottomSheet with Google Maps for iOS View it does not let me to drag up or down the BottomSheet, even if it adds (it's visible on the screen).
Code: As already mentioned I have a Google Maps View and my BottomSheet View:
public override void ViewDidLoad()
{
base.ViewDidLoad();
SetMap();
SetBottomSheet();
}
private void SetMap()
{
var frame = new CGRect(0, View.Frame.GetMaxY(), View.Frame.Width, View.Frame.Height);
mapView = new MapView(View.Frame);
mapView.MyLocationEnabled = true;
mapView.BuildingsEnabled = false;
mapView.SetMinMaxZoom(15f, 18f);
View = mapView;
}
private void SetBottomSheet()
{
var bottom = UIScreen.MainScreen.Bounds.Height - UIApplication.SharedApplication.StatusBarFrame.Height;
bottomSheetViewController = new BottomSheetViewController(100, 300, bottom, true, BottomSheetState.Middle);
AddChildViewController(bottomSheetViewController);
View.AddSubview(bottomSheetViewController.View);
bottomSheetViewController.DidMoveToParentViewController(this);
bottomSheetViewController.View.Frame = new CGRect(0, View.Frame.GetMaxY(), View.Frame.Width, View.Frame.Height);
}
How can I wire these two views (mapView and bottomSheetViewController.View) in order to not lose the drag up/down feature from the BottomSheet control?
Even if the question is for Xamarin, an answer from swift or objective-c will do just fine as I will do the 'translation' afterwards.
I had a similar project, my solution at that time was to set the ConsumeGesturesInView property to false.
So your SetMap() method should look like this:
private void SetMap()
{
var frame = new CGRect(0, View.Frame.GetMaxY(), View.Frame.Width, View.Frame.Height);
mapView = new MapView(View.Frame);
mapView.MyLocationEnabled = true;
mapView.Settings.ConsumeGesturesInView = false;
mapView.BuildingsEnabled = false;
mapView.SetMinMaxZoom(15f, 18f);
View = mapView;
}

Xamarin iOS Center TableViewCell Subview

I´m using the NuGet-Package iCarousel as a Subview in UITableViewCell.
Here is my code where I add the iCarousel object (this code is located in the ViewDidLoad method of my parent ViewController):
InvokeOnMainThread(async() =>
{
items = await ppCtrl.GetAllPerpetrationPartIds(masterProject.Id).ConfigureAwait(false);
if (items != null && items.Any())
{
InvokeOnMainThread(() =>
{
var carousel = new iCarousel
{
Bounds = CarouselViewCell.ContentView.Bounds,
ContentMode = UIViewContentMode.Center,
Type = iCarouselType.CoverFlow2,
Frame = CarouselViewCell.ContentView.Frame,
CenterItemWhenSelected = true,
DataSource = new SimpleDataSource(items, CarouselViewCell.ContentView.Bounds.Width, CarouselViewCell.ContentView.Bounds.Height),
Delegate = new SimpleDelegate(this)
};
NSLayoutConstraint centerX = carousel.CenterXAnchor.ConstraintEqualTo(this.CarouselViewCell.ContentView.CenterXAnchor);
NSLayoutConstraint centerY = carousel.CenterYAnchor.ConstraintEqualTo(this.CarouselViewCell.ContentView.CenterYAnchor);
centerX.SetIdentifier("centerXCostraint");
centerY.SetIdentifier("centerYConstraint");
var centerConstraints = new[] {centerX, centerY };
CarouselViewCell.ContentView.TranslatesAutoresizingMaskIntoConstraints = false;
CarouselViewCell.ContentView.AddConstraints(centerConstraints);
//NSLayoutConstraint.ActivateConstraints(centerConstraints);
CarouselViewCell.ContentView.ContentMode = UIViewContentMode.Center;
CarouselViewCell.ContentView.AddSubview(carousel);
ViewDidLayoutSubviews();
});
}
});
Now I want to vertically and horizontally center the iCarousel. So I added this code:
NSLayoutConstraint centerX = carousel.CenterXAnchor.ConstraintEqualTo(this.CarouselViewCell.ContentView.CenterXAnchor);
NSLayoutConstraint centerY = carousel.CenterYAnchor.ConstraintEqualTo(this.CarouselViewCell.ContentView.CenterYAnchor);
centerX.SetIdentifier("centerXCostraint");
centerY.SetIdentifier("centerYConstraint");
var centerConstraints = new[] {centerX, centerY };
CarouselViewCell.ContentView.TranslatesAutoresizingMaskIntoConstraints = false;
CarouselViewCell.ContentView.AddConstraints(centerConstraints);
My problem is now that the app Crashes at this line:
CarouselViewCell.ContentView.AddConstraints(centerConstraints);
I found in a similar post that you need to add the constraints to the assiciated UIView object. I tried several objects but with no success.
CarouselViewCell.ContentView.AddConstraints(centerConstraints);
This code will crash because we need to add the control first, then set its constraints. Or it will crash. Try to adjust the code's order:
//Let this code just lie after the carousel's construction.
CarouselViewCell.ContentView.AddSubview(carousel);
Also if you want to add the carousel's constraints, you should set the carousel's TranslatesAutoresizingMaskIntoConstraints to false not CarouselViewCell.ContentView.
Moreover depending on your description, I think you want to put an iCarousel on UITableViewCell. This configuration should be set in the Cell class not in the parent ViewController class.

Autolayouts in UIScrollView using Cirrious.FluentLayouts.Touch

I've to do a ViewController with autolayouts in scrollView, but here is few problems:
public SomeVC() : UIViewController
{
_mainScrollView = new UIScrollView {
ShowsHorizontalScrollIndicator = false,
ShowsVerticalScrollIndicator = true,
BackgroundColor = UIColor.Clear,
ScrollEnabled = true,
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
TranslatesAutoresizingMaskIntoConstraints = true
};
_userDataTableView = new UITableView(CGRect.Empty, UITableViewStyle.Grouped);
_userDataTableView.LayoutIfNeeded();
_saveButton = new UIButton();
_menuTableView = new UITableView(CGRect.Empty, UITableViewStyle.Grouped);
_menuTableView.LayoutIfNeeded();
_logoutButton = new UIButton();
}
public override void LoadView()
{
base.LoadView();
View = _mainScrollView;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Add(_userDataTableView);
Add(_saveButton);
Add(_menuTableView);
Add(_logoutButton);
_mainScrollView.AddConstraints(
_userDataTableView.AtTopOf(View),
_userDataTableView.AtLeftOf(View),
_userDataTableView.AtRightOf(View),
_userDataTableView.Height().EqualTo(_userDataTableView.ContentSize.Height),
_saveButton.Below(_userDataTableView, 20),
_saveButton.AtLeftOf(_mainScrollView, 10),
_saveButton.AtRightOf(_mainScrollView, 10),
_saveButton.Height().EqualTo(44),
_menuTableView.Below(_saveButton, 20),
_menuTableView.AtLeftOf(_mainScrollView),
_menuTableView.AtRightOf(_mainScrollView),
_menuTableView.Height().EqualTo(_menuTableView.ContentSize.Height),
_logoutButton.Below(_menuTableView, 20),
_logoutButton.AtLeftOf(_mainScrollView, 10),
_logoutButton.AtRightOf(_mainScrollView, 10),
_logoutButton.Height().EqualTo(44)
);
_mainScrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
}
In fact, it works, but contents width is about half of screen width, and scrolling not working. How to get it works?
As far as I understand, the problem is - _mainScrollView.ContentSize, but how and where should I set it, when using autolayouts?
If your views doesn't exceed the screen you will not be able to do scroll. if you only have something like this :
// Create containers
contentView = new UIView();
scrollView = new UIScrollView { contentView };
Add(scrollView);
contentView.AddSubviews(logo, user, password, loginButton);
// Auto layout
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(scrollView.FullWidthOf(View));
View.AddConstraints(scrollView.FullHeightOf(View));
View.AddConstraints(
contentView.WithSameWidth(View),
contentView.WithSameHeight(View).SetPriority(UILayoutPriority.DefaultLow)
);
scrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
scrollView.AddConstraints(contentView.FullWidthOf(scrollView));
scrollView.AddConstraints(contentView.FullHeightOf(scrollView));
// very important to make scrolling work
var bottomViewConstraint = contentView.Subviews.Last()
.AtBottomOf(contentView).Minus(20);
contentView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
contentView.AddConstraints(
logo.AtTopOf(contentView),
logo.WithRelativeWidth(contentView, 0.8f),
logo.WithSameCenterX(contentView),
logo.WithRelativeHeight(contentView, 0.3f),
user.Below(logo, 50),
user.WithRelativeWidth(logo, 0.8f),
user.WithSameCenterX(logo),
password.Below(user),
password.WithSameWidth(user),
password.WithSameCenterX(user),
loginButton.Below(password, 50),
loginButton.WithRelativeWidth(password, 0.9f),
loginButton.Height().EqualTo(50),
loginButton.WithSameCenterX(password)
);
contentView.AddConstraints(bottomViewConstraint);
}
I'm using this package and it works perfectly like if I was using a stackLayout inside a scrollview in xamarin.forms I think is the perfect behaviour.
Xamarin.IQKeyboardManager from Nuget
Also, if you want to center your content view inside the scrollview you will need to add this:
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
var scrollViewBounds = scrollView.Bounds;
var containerViewBounds = contentView.Bounds;
var scrollViewInsets = UIEdgeInsets.Zero;
scrollViewInsets.Top = scrollViewBounds.Size.Height / 2.0f;
scrollViewInsets.Top -= contentView.Bounds.Size.Height / 2.0f;
scrollViewInsets.Bottom = scrollViewBounds.Size.Height / 2.0f;
scrollViewInsets.Bottom -= contentView.Bounds.Size.Height / 2.0f;
scrollViewInsets.Bottom += 1;
scrollView.ContentInset = scrollViewInsets;
}
And thats all, doesn't matter how your contentView is . You will have a centered contetnview inside a scrollview and a manager to capture the keyboard events and adapt your view to this event .
Solution found:
Firstly:
_userDataTableView.AtLeftOf(View),
_userDataTableView.AtRightOf(View),
is not valid, instead of this, we should use:
_userDataTableView.AtLeftOf(View),
_userDataTableView.WithSameWidth(View),
if we want margin, we just add
_userDataTableView.WithSameWidth(View).Minus(MARGIN)
And the last thing we have to do:
_logoutButton.Height().EqualTo(44),
_logoutButton.Bottom().EqualTo().BottomOf(_mainScrollView).Plus(10)
last line is very important. It says to scrollView right content size.

Can't get UITableView to scroll inside my SlidingPanel

I've got a UITableView located in a View attached to a SlidingPanel.
I'm using SlidingPanels.Lib, and I've got a custom presenter
Here's a gist of the custom presenter
From there my MenuView is really straight forward.
public class MenuView : ViewControllerBase
{
private new MenuViewModel ViewModel { get { return (MenuViewModel)base.ViewModel; } }
public override void ViewDidLoad()
{
base.ViewDidLoad();
// todo: this should actually be...
// _currentBookId = currentUser.UserBooks[0].BookId;
// _homeViewModel.ChapterViewModel = Mvx.Resolve<IChapterService>().FindByBookId(_currentBookId);
ViewModel.Chapters = Mvx.Resolve<IChapterService>().Find();
// this ensures the sliding panel doesn't fill the entire view.
var frame = View.Frame;
frame.Width = 300;
View.Frame = frame;
// var currentUser = Mvx.Resolve<IUserService>().GetById(Mvx.Resolve<UserModel>().Id);
var label = new UILabel(new RectangleF(10, 10, 300, 40))
{
TextColor = UIColor.White,
};
Add(label);
//var listHeight = (chapters.Count*40);
var navigationList = new UITableView(new RectangleF(0, 50, 300, 300))
{
Source = new NavigationTableSource(ViewModel.Chapters)
};
Add(navigationList);
var set = this.CreateBindingSet<MenuView, MenuViewModel>();
set.Bind(label).To(vm => vm.DisplayName);
set.Apply();
}
}
Unfortunately I'm not sure where to look in order to allow the TableView to scroll. I can see it, but it goes beyond the bottom of the screen.
I think the problem is in:
var listHeight = (chapters.Count*40);
Instead of this, try setting the height to the height of the available screen (which depends on which iPhone/iPad you are on). After this, then the list will scroll within that available height.
Took a couple of hours of dicking around to discover that the original source code that I grabbed from #patbonecrusher's Github repo had some differences from the lib that you can find at #fcaico's Github repo
Long story short... all I had to do was compile #fcaico's version and deploy it into my app, and the scrolling came back. Now I realize that #fcaico's repo just contains a submodule to #patbonecrusher's, but for some reason... the recompile fixed the issue. Don't have a lot of time to dig into the reason why.

Resources