BUG: Hide / Show multiple views arranged in stack view at once - ios

We wanted to hide and show single or multiple views arranged in stack view whenever it is required. So, all what we did is simply:
stackView.arrangedSubviews[0].isHidden = false
stackView.arrangedSubviews[1].isHidden = true
stackView.arrangedSubviews[2].isHidden = false
However, the result is not as expected where stack view or views arranged in the stack view ignores in some cases to be hidden or shown!!
We've found this workaround to fix hide / show multiple views arranged in stack view:
extension UIView {
var isHiddenInStackView: Bool {
get {
return isHidden
}
set {
if isHidden != newValue {
isHidden = newValue
}
}
}
}
Surprisingly, this workaround is working fine but we're wondering if anything else more reliable solution that we can do to avoid causing this bug again later in the future updates?

Related

AnimatedVisibility and BringIntoViewRequester not working together

I'm building an expandable Composable which would be expanded when clicked.
This would be implemented by using the AnimatedVisibility which works perfectly.
Code for the visibility animation:
AnimatedVisibility(
visible = isExpanded,
) {
// Content removed.
}
The problem I'm currently facing is that this is located in a vertical scrollable column and it should scroll to the expanded content when clicked next to expanding it.
As I read this would be done by using the BringIntoViewRequester as in the code snippet below:
var isExpanded by remember { mutableStateOf(false) }
val intoViewRequester = remember { BringIntoViewRequester() }
ClickableComposable(modifier = Modifier.clickable {
isExpanded = !isExpanded
if(isExpanded) {
coroutineScope.launch {
// delay(200)
intoViewRequester.bringIntoView(rect = null)
}
}
})
AnimatedVisibility(
modifier = Modifier.bringIntoViewRequester(intoViewRequester),
visible = isExpanded,
) {
// Content removed.
}
The code above works with the delay but that's not a perfect interaction for the user. To first see the content expanding and afterwards see the page scroll. The ideal situation would be that it would happen at the same time, however the content is not yet measured in any way. By removing the delay it does not work as the content is not yet visible.
Is there anything in Compose to do the expanding and scrolling to at the same time?

Animating UIStackView subviews causes layout issues

I have a UIStackView with several arranged views inside, which can be shown and hidden using buttons.
I animate the changes by:
- initially setting the alpha to 0 and isHidden = true of some subviews
- in an animate block, switch which subview has alpha = 1 and isHidden = false
I created a playground to show the issue: https://gist.github.com/krummler/d0e8db8cb037ae7202f7d801d3114111
In short, this works fine for two views: switching between ANY two subviews works fine. When pressing a third, the view collapses and refuses to come back. After that, showing subviews becomes a mess. Interestly, it does not show this behaviour when animations are commented out.
My questions:
- Am I missing something or did I hit some bug in UIKit?
- How can I work around this or is there a better wat to achieve what I am trying to do?
It is hard to say how UIStackView is implemented, but it may be attempting to update its layout when isHidden is modified even though the value is not actually changing.
Perhaps this is a UIKit bug, but as a workaround you can modify your resetSubviews(to:) implementation so that it only sets isHidden when the state is actually changing.
private func resetSubviews(to view: UIView) {
view1.alpha = view == view1 ? 1 : 0
view2.alpha = view == view2 ? 1 : 0
view3.alpha = view == view3 ? 1 : 0
view4.alpha = view == view4 ? 1 : 0
let updateIsHiddenForView = { (viewToUpdate: UIView) in
let isHidden = view != viewToUpdate
if isHidden != viewToUpdate.isHidden {
viewToUpdate.isHidden = isHidden
}
}
updateIsHiddenForView(view1)
updateIsHiddenForView(view2)
updateIsHiddenForView(view3)
updateIsHiddenForView(view4)
}

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.

Hide one UIView and show another

When I click on an a segment of my UISegmentedControl, I want one of two UIViews to be displayed. But how do I arrange it, that I only show the new view. Currently I am calling thisview.removeFromSuperview() on the old one, and then setup the new all from scratch. I also tried setting all the HeightConstants of the views subviews to zero and then set the heightConstants of the view itself to zero but I'd rather avoid that constraints-surgery..
What better approaches are there?
Agree with #rmaddy about using UIView's hidden property, a nice simple way to cause a view to not be drawn but still occupy its place in the view hierarchy and constraint system.
You can achieve a simple animation to make it a bit less jarring as follows:
UIView.animate(withDuration:0.4, animations: {
myView.alpha = 0
}) { (result: Bool) in
myView.isHidden = true
}
This will fade the alpha on the view "myView", then upon completion set it to hidden.
The same animation concept can be used also if you've views need to re-arrange themselves, animating layout changes will be a nice touch.
Based on #rmaddy and #CSmiths answer, I built the following function:
func changeView(newView: UIView, oldView: UIView) {
newView.isHidden = false
newView.alpha = 0
UIView.animate(withDuration:0.4, animations: {
oldView.alpha = 0
newView.alpha = 1
}) { (result: Bool) in
oldView.isHidden = true
}
}
I feel dumb now for all the hours I spent on that constraint-surgery. :|

Adding and removing UIStackViews messing up my UIScrollView

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

Resources