Adding and removing UIStackViews messing up my UIScrollView - ios

Whenever I use the pickerview to switch views from Auto Rent to Schedule Rent it works perfectly. It is when I switch from Schedule Rent to Auto Rent that this black bar appears. I have attached the hierarchy of my content view. I thought it had to do with previous constraints added, so I remove a StackView whenever one view is chosen. For example, if Auto Rent is chosen, then I remove the StackView where the Schedule View is in:
//Holds Temp Stackviews
var stackViewHolder1: UIView?
var stackViewHolder2: UIView?
override func viewDidLoad() {
super.viewDidLoad()
stackViewHolder1 = stackViewMain.arrangedSubviews[0]
stackViewHolder2 = stackViewMain.arrangedSubviews[1]
}
if txtRentType.text == "Auto Rent" {
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView!.isHidden = true
stackViewMain.removeArrangedSubview(tempView!)
if(tempView1!.isHidden == true){
tempView1!.isHidden = false
stackViewMain.addArrangedSubview(tempView1!)
}
else{
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView1!.isHidden = true
stackViewMain.removeArrangedSubview(tempView1!)
if(tempView!.isHidden == true){
tempView!.isHidden = false
stackViewMain.addArrangedSubview(tempView!)
}
}
I have tried deleting one view and toggling only one view has being hidden and that removes the black bar issue. There is no constraint with the stackViews and Content View.
EDIT:
The screen highlighted is the scrollView. The one after is the contentView. UIWindow goes black in the back.
My Title Bar at the top ends up in the middle somehow.

You can try to modify your stack distribution property
stack.distribution = .equalCentering
After you won't need to use this:
.removeArrangedSubview()
.addArrangedSubview()
When you hide some view, the other view take all space of your stack, you don't need to update your constraints. You can try it on interface builder to see how it works.

are you pinning your scrollview and the content view to the bottom with constraints?
If the content view is a stack view you can pin it to the bottom as well with layout constraints and play with the content distribution.

You don't need to use remove/Add arranged subviews.
when hiding a view in a stackView its automatically removed.
so i think you can just hide or show the stackViewMain.subviews[0] o stackViewMain.subviews[1]
i'm with objc maybe i do a mistake but it would be something like this :
if txtRentType.text == "Auto Rent" {
stackViewMain.arrangedSubviews[0].isHidden = true;
stackViewMain.arrangedSubviews[1].isHidden = false;
}else{
stackViewMain.arrangedSubviews[1].isHidden = true;
stackViewMain.arrangedSubviews[0].isHidden = false;
}

Related

UISplitViewController - Expand & Collapse Master View in iPad Portrait

My universal app displays both master and detail views in iPad with preferredDisplayMode = .allVisible. I need to expand the master view into full screen and hide the detail view on a button click. I know there is functionality to expand detail into full screen hiding master. But couldn't find how to expand and collapse master view.
I tried in expand function as below.
self.splitViewController.preferredPrimaryColumnWidthFraction = 1.0
self.splitViewController.maximumPrimaryColumnWidth = self.splitViewController.view.bounds.size.width as! CGFloat
And collapse function as below.
self.splitViewController.preferredDisplayMode = .allVisible
self.splitViewController.preferredPrimaryColumnWidthFraction = 0.6
self.splitViewController.maximumPrimaryColumnWidth = self.splitViewController.view.bounds.size.width as! CGFloat
But they don't work. Any ideas on how to achieve this?
My setup uses a navigation bar, but the show/hide functionality would probably work without it. What I wanted to achieve is to have the user decide whether to show the "primary" view and have it shown no matter the orientation - something a UISplitViewController doesn't natively do.
I achieved this through activating/deactivating two arrays of constraints, pinning the secondary" view's leading anchor to the "primary's" trailing anchor. From there, all the constraint changes are with the "primary" view.
(I'm using quotation marks because these view's actually have much more logic that a UIView should have, so they are each UIViewControllers with one being a child of the other, but the constraints are view-related.)
Let's start with the static constraints:
primary.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
primary.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
primary.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
secondary.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
secondary.leadingAnchor.constraint(equalTo: toolBar.trailingAnchor).isActive = true
secondaary.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
secondary.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
Next, define/populate the two arrays that will show/hide the primary:
var isShowingPrimary = false
var showPrimary = [NSLayoutConstraint]()
var hidePrimary = [NSLayoutConstraint]()
showPrimary.append(primary.widthAnchor.constraint(equalToConstant: 300))
hidePrimary.append(primary.widthAnchor.constraint(equalToConstant: 0))
NSLayoutConstraint.activate(hidePrimary)
Notice that isActive is true for the static constraints, and I activated the hidePrimary constraints only.
Now all you need to do is wire up a UIBarButtonItem or UIButton to execute a toggle function that will show/hide the primary view, along with animating it:
func togglePrimary() {
if isShowingPrimary {
// hide primary view
NSLayoutConstraint.deactivate(showPrimary)
NSLayoutConstraint.activate(hidePrimary)
} else {
// show primary view
NSLayoutConstraint.deactivate(hidePrimary)
NSLayoutConstraint.activate(showPrimary)
}
// toggle flag and animate changes
isShowingPrimary.toggle()
UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
}

How do I limit the size of a UIView in a storyboard designer and the runtime view overall?

I have a custom UIView that's my little component lets call it PlaceholderView.
My UITableViewCell prototype has a Label and a PlaceholderView that sits inside a UIStackView that's vertically axis.
The PlaceholderView, is supposed to call some custom code that goes to a cache to retrieve a specific view then it adds it to the SubView of the PlaceholderView.
I want this subview to take up the whole surface of the entire PlaceholderView. How do I go about doing that? I tried this but not sure if it does the job
if (view != null)
{
AddSubview(view);
view.SizeToFit();
}
Second question. These view's that I am adding, when I create them during design time, I make a new storyboard, drag and drop a ViewController then proceed to place other controls like Labels and Button's on it.
How do I restrict this ViewController's overall height so it's completely fixed size? I know I can set the simulated metrics, and I am also setting the View. Frame's size to restrict the height.
Are there better ways to make these views constrained easier?
Currently, when I am setting these fixed height values, it does cause some weird issues with overlaps if I set UITableView.RowHeigh to AutomaticDimension.
// attaches all sides of the child to its parent view
extension UIView {
func layoutToSuperview() {
guard let view = self.superview else {return}
self.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
self.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
self.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
}
}
Usage:
Instead of view.SizeToFit() use view.layoutToSuperview()
I want this subview to take up the whole surface of the entire
PlaceholderView
You can try autolayout:
if (view != null)
{
view.TranslatesAutoresizingMaskIntoConstraints = false;
AddSubview(view);
view.LeadingAnchor.ConstraintEqualTo(this.LeadingAnchor).Active = true;
view.TopAnchor.ConstraintEqualTo(this.TopAnchor).Active = true;
view.TrailingAnchor.ConstraintEqualTo(this.TrailingAnchor).Active = true;
view.BottomAnchor.ConstraintEqualTo(this.BottomAnchor).Active = true;
}
Are there better ways to make these views constrained easier?
The answer is also auto layout. Set a control's height constraint to claim how much space it wants to take. See this thread for more details: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights.
Though it is native oc, you can see its concept.

ios using constraints to hide a view

Why is it that if I have a simple viewcontroller with a button, a "control view" and a tableview below it, I can't get the logic to work that will expand the tableview to cover the "view" when the button is pressed and then restore if pressed again. The table will overlay the "control view" but when pressed a second time, does not "restore" the table below the "control view"
#IBAction func buttonPressed(_ sender: Any) {
if bControlHide == false {
myTableView.translatesAutoresizingMaskIntoConstraints = false
myControlView.translatesAutoresizingMaskIntoConstraints = false
myTableView.topAnchor.constraint(
equalTo: myControlView.topAnchor).isActive = true
bControlHide = true
}
else {
bControlHide = false
myTableView.translatesAutoresizingMaskIntoConstraints = false
myControlView.translatesAutoresizingMaskIntoConstraints = false
myTableView.topAnchor.constraint(
equalTo: myControlView.bottomAnchor).isActive = true
}
}
If your controlView has fixed height,
1) Make an outlet for the controlView height from storyboard to ViewController and set it to 0 when you need to hide it. Set another value when you unhide.
Do not forget to set 0 controlView bottom space to tableView, otherwise tableView will not cover your controlView.
You should set the height of tableview and also tableview should be constrained to top and bottom vertical spacing(if you want to support different devices).
Initial setup would be:
height constant is greater than 0
height priority is 1000
bottom constraint is 0
bottom priority is 750
When you tap to button you just need to change priorities:
height priority is 750
bottom priority is 1000
Please pay attention to the following codes:
myTableView.topAnchor.constraint(equalTo:myControlView.topAnchor).isActive = true
myTableView.topAnchor.constraint(equalTo: myControlView.bottomAnchor).isActive = true
Every time you call myTableView.topAnchor.constraint will add a new constraint, this is not what you want.
So please create and save the two constraints to member variables, and then activate or deactivate it according to your needs.
BTW, you don't have to set translatesAutoresizingMaskIntoConstraints every time when you click button, set it once in xib or in viewDidLoad

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

How to modify view controller after pressing a button?

Let's say that if you press the button from the bottom of the screen after typing something in the account text field, you would be required to also enter the password like in the second image.
How should I do this? I don't think that creating a new view controller would be good. So, should I somehow modify the same view controller?
How could I add the new password text field under the account text field?
Keep in mind that they are still centered. Hiding and unhiding wouldn't work in this case and I also need to modify more things than only adding that text field.
First, create a UIView with everything you need on them. in this example I will have only two text fields and they are all color coded.
The view needs to be centered both horizontally and vertically, with width and height. Set identifiler for the height constraint to be updated later. Set the clip to board to true so that when we redeuce the height of the view, text fields below will hide. The settings for the view will be like this
For your text fields, they must have a constant padding to top. In my example, they are set to be in center horizontally, having a constant height, width and padding to opp.
Now, all you need to do is to get the height of the view from code and set the height to show or hide the text fields.
var flag = true
#IBAction func click(_ sender: Any) {
if flag {
flag = false
let filteredConstraints = theView.constraints.filter { $0.identifier == "viewHeight" }
if let heightConstraint = filteredConstraints.first {
heightConstraint.constant = 60
}
} else {
flag = true
let filteredConstraints = theView.constraints.filter { $0.identifier == "viewHeight" }
if let heightConstraint = filteredConstraints.first {
heightConstraint.constant = 128
}
}
}
Here is the code running in simulater.
another option is you can make tableView at Center, you need to create tableview height constraint outlet,
then you can maintain a counter how many time you want to add View, the counter should be return in tableView numberOfRowsInSection,
and you can make this view in Prototype Cell or using NIB, then just adjust header label text, textField placeholder and text on specific cell index,
when you increase or decrease counter update tableView Constraint, example you have a cell height as 50, in first case when you have on cell in tableView, you can set your tableView height constraint's constant as 50, when cells are two the 100 and so on.....
I think it's a simple logic

Resources