Autolayouts in UIScrollView using Cirrious.FluentLayouts.Touch - ios

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.

Related

Vertical UIStackView alignment

I have a vertical UIStackview with 3 controls in it.
I want all the controls to be centered, and the first two to expand the whole width available. The third one should take 80% of the available width and be centered as well.
Here is the result I got so far:
As you can see the third control is left aligned.
Here is the code I have for all of this:
var container = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Vertical,
Distribution = UIStackViewDistribution.EqualCentering,
Alignment = UIStackViewAlignment.Fill,
Spacing = 10f
};
// Create the Title
UILabel title = new UILabel
{
TranslatesAutoresizingMaskIntoConstraints = false,
Text = item.name,
TextAlignment = UITextAlignment.Center,
Lines = 2,
};
container.AddArrangedSubview(title);
// Create the gauge
SFCircularGauge circularGauge = new SFCircularGauge { TranslatesAutoresizingMaskIntoConstraints = false };
circularGauge.Tag = circularGauge.GenerateViewTag();
circularGauge.HeightAnchor.ConstraintEqualTo(100f).Active = true;
#if DEBUG
circularGauge.BackgroundColor = UIColor.Cyan;
#endif
container.AddArrangedSubview(circularGauge);
// Add the evaluate button
var evaluate = UIButton.FromType(UIButtonType.RoundedRect);
evaluate.TranslatesAutoresizingMaskIntoConstraints = false;
evaluate.SetTitle(Utilities.GetLocalizedString("qol_rate_button_text"), UIControlState.Normal);
evaluate.SetTitleColor(UIColor.White, UIControlState.Normal);
evaluate.BackgroundColor = new UIColor(red: 1.00f, green: 0.37f, blue: 0.00f, alpha: 1.0f); // Optimistic Orange
evaluate.Layer.CornerRadius = 5f;
evaluate.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
evaluate.ContentEdgeInsets = new UIEdgeInsets(top: 0, left: 10f, bottom: 0, right: 10f);
evaluate.UserInteractionEnabled = true;
evaluate.TouchUpInside -= Evaluate_TouchUpInside;
evaluate.TouchUpInside += Evaluate_TouchUpInside;
evaluate.Tag = evaluate.GenerateViewTag();
viewIdsAutoEvalsIds.Add((int)evaluate.Tag, item.id);
container.AddArrangedSubview(evaluate);
evaluate.WidthAnchor.ConstraintEqualTo(container.WidthAnchor, 0.8f).Active = true;
evaluate.CenterXAnchor.ConstraintEqualTo(container.CenterXAnchor).Active = true;
I can't figure out where is my problem. In the UIStackView configuration? Somewhere else?
Thanks in advance.
The property Alignment is to sets the alignment of the non-axial subviews . In your case , you need to set it as
UIStackViewAlignment.Center
instead of
UIStackViewAlignment.Fill
This might be a bug of the control. We'll see.
In the meantime, thanks again to the Reveal Tool, the gauge has no width defined.
I solved the problem by setting the width constraint explicitly:
// Fix the width to be 100% of the container
circularGauge.WidthAnchor.ConstraintEqualTo(container.WidthAnchor).Active = true;

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

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)
);
}

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.

Xamarin iOS - Fit scrollview's contents within the scrollview

This may be a fairly simple solution but none of the suggestions here work for me. I have a UIScrollView inside a UITableViewCell. I'm adding dynamic images from a list into the scrollview like this.
public void InitViews()
{
scrollViewThumbNails.ContentSize =
new SizeF((float)scrollViewThumbNails.Frame.Size.Width/listCount * listCount,
(float)scrollViewThumbNails.Frame.Size.Height);
for (int i = 0; i < listCount; i++)
{
var imageView = new UIImageView
{
Frame = new RectangleF((float)i * (float)scrollViewThumbNails.Frame.Size.Width / listCount, 0,
(float)scrollViewThumbNails.Frame.Size.Width / listCount, (float)scrollViewThumbNails.Frame.Size.Height),
UserInteractionEnabled = true,
ContentMode = UIViewContentMode.ScaleAspectFit
};
//call method to load the images
var index = i;
imageView.SetImage(
url: new NSUrl(allItems[i].AbsoluteUri),
placeholder: UIImage.FromFile("placeholder.png"),
completedBlock: (image, error, type, url) =>
{
//when download completes add it to the list
if (image != null)
{
allImages.Add(image);
scrollViewThumbNails.AddSubview(imageView);
}
});
}
I found a suggestion here which advices to set the content size of the scrollview, based on the total number of subviews in ViewDidAppear like this:
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CGRect contentRect = CGRect.Empty;
foreach(UIImageView view in scrollViewThumbNails.Subviews)
{
contentRect = CGRect.Union(contentRect,view.Frame);
}
scrollViewThumbNails.ContentSize = contentRect.Size;
}
The imageViews are still spaced out and do not start from the edge of the screen/scrollview as shown in my screen shot below. I would like for the first image to always be positioned at the origin of the scrollview and wouldn't want the spacing between each image. How can I adjust the scrollview based on the size of its contents?
Can someone show me what I'm missing? Thanks.
I found a solution here where I calculate the total height and width of all the views and assign that as the content size of the scroll view in ViewDidLayoutSubviews:
Solution
public override void ViewDidLayoutSubviews ()
{
base.ViewDidLayoutSubviews ();
try{
float scrollViewHeight = 0.0f;
float scrollViewWidth = 0.0f;
foreach(UIImageView view in scrollViewThumbnails.Subviews)
{
scrollViewWidth += (float)view.Frame.Size.Width;
scrollViewHeight += (float)view.Frame.Size.Height;
}
scrollViewThumbnails.ContentSize = new CGSize(scrollViewWidth, scrollViewHeight);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message+ex.StackTrace);
}
}

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