I am using a segmented control to rotate through content, but when I scroll to the end of a subview and switch to another segment, the new segment shows roughly in the same position as the previous segment (i.e. if I scroll to the bottom of segment A and switch segments, segment B is also at the bottom of the scroll). How can I reset each segment so that it is positioned at the top each time I change segments?
Sample code:
class AboutUsViewController: UIViewController {
#IBOutlet weak var welcomeContainer: UIView!
var views: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
views = [UIView]()
views.append(ViewController1().view)
views.append(ViewController2().view)
views.append(ViewController3().view)
views.append(ViewController4().view)
for view in views {
welcomeContainer.addSubview(view)
}
welcomeContainer.bringSubview(toFront: views[0])
}
#IBAction func swichViewAction(_ sender: UISegmentedControl) {
self.welcomeContainer.bringSubview(toFront: views[sender.selectedSegmentIndex])
}
}
Related
I notice that, if I perform add/ expand animation within an UIScrollView, it will cause unwanted scrolling behavior, when the UIScrollView fill with enough content to become scroll-able.
As you can see in the following animation, initially, the add/ expand animation works just fine.
When we have added enough item till the UIScrollView scrollable, whenever a new item is added, and UIScrollView will first perform scroll down, and then scroll up again!
My expectation is that, the UIScrollView should remain static, when add/ expand animation is performed.
Here's the code which performs add/ expand animation.
Add/ expand animation
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
// Clear off horizontal swipe in animation caused by addArrangedSubview
stackView.superview?.layoutIfNeeded()
customView.show()
// Perform expand animation.
UIView.animate(withDuration: 1) {
self.stackView.superview?.layoutIfNeeded()
}
}
Here's the constraint setup of the UIScrollView & added custom view item
Constraint setup
Custom view
class CustomView: UIView {
private var zeroHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.cornerRadius = stackView.frame.height / 2
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
zeroHeightConstraint = self.safeAreaLayoutGuide.heightAnchor.constraint(equalToConstant: 0)
zeroHeightConstraint.isActive = false
}
func hide() {
zeroHeightConstraint.isActive = true
}
func show() {
zeroHeightConstraint.isActive = false
}
}
Here's the complete source code
https://github.com/yccheok/add-expand-animation-in-scroll-view
Do you have any idea why such problem occur, and we can fix such? Thanks.
Because of the way stack views arrange their subviews, animation can be problematic.
One approach that you may find works better is to embed the stack view in a "container" view.
That way, you can use the .isHidden property when adding an arranged subview, and allow the animation to update the "container" view:
The "add view" function now becomes (I added a Bool so we can skip the animation on the initial add in viewDidLoad()):
func addCustomView(_ animated: Bool) {
let customView = CustomView.instanceFromNib()
stackView.addArrangedSubview(customView)
customView.isHidden = true
if animated {
DispatchQueue.main.async {
UIView.animate(withDuration: 1) {
customView.isHidden = false
}
}
} else {
customView.isHidden = false
}
}
And we can get rid of all of the hide() / show() and zeroHeightConstraint in the custom view class:
class CustomView: UIView {
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
borderView.layer.cornerRadius = borderView.bounds.height * 0.5
}
}
Since it's a bit difficult to clearly show everything here, I forked your project with the changes: https://github.com/DonMag/add-expand-animation-in-scroll-view
Edit
Another "quirk" of animating a stack view shows up when adding the first arranged subview (also, when removing the last one).
One way to get around that is to add an empty view as the first subview.
So, for this example, in viewDidLoad() before adding an instance of CustomView:
let v = UIView()
stackView.addArrangedSubview(v)
This will make the first arranged subview a zero-height view (so it won't be visible).
Then, if you're implementing removing custom views, just make sure you don't remove that first, empty view.
If your stack view has .spacing = 0 noting else is needed.
If your stack view has a non-zero spacing, add another line:
let v = UIView()
stackView.addArrangedSubview(v)
stackView.setCustomSpacing(0, after: v)
I did a little research on this and the consensus was to update the isHidden and alpha properties when inserting a view with animations.
In CustomView:
func hide() {
alpha = 0.0
isHidden = true
zeroHeightConstraint.isActive = true
}
func show() {
alpha = 1.0
isHidden = false
zeroHeightConstraint.isActive = false
}
In your view controller:
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
self.stackView.layoutIfNeeded()
UIView.animate(withDuration: 00.5) {
customView.show()
self.stackView.layoutIfNeeded()
}
}
Also, the constraints in your storyboard aren't totally correct. You are seeing a red constraint error because autolayout doesn't know the height of your stackView. You can give it a fake height and make sure that "Remove at build time" is checked.
Also, get rid of your scrollView contentView height constraint defined as View.height >= Frame Layout Guide.height. Autolayout doesn't need to know the height, it just needs to know how subviews inside of the contentView stack up to define its vertical content size.
Everything else looks pretty good.
I cant create saperate custom popview.. so I have tried like below mentioned one of the answer
here is storyboard view hierarchy
popBG view background colour is black with alpha = 0.3
this is code:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var popViewtop: UIView!
#IBOutlet weak var testTable: UITableView!
#IBOutlet weak var popView: UIView!
#IBOutlet weak var popBG: UIView!
override func viewDidLoad() {
super.viewDidLoad()
popViewtop.isHidden = true
}
#IBAction func testBtn(_ sender: Any) {
popViewtop.isHidden = false
}
#IBAction func btnPop(_ sender: Any) {
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NewZoomAddressViewController") as! NewZoomAddressViewController;
popViewtop.isHidden = true
self.navigationController?.pushViewController(viewController, animated: true);
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
}
now the tableview and button are not showing transperently.. only popbg and popview coming.. did i miss anything..
Actually i need like this: total backgroundview in some darkcolour and popupview heighlighted in white colour
if giving alpha value to view,
view's subView's alpha value changes, including popupView
if giving background color to view, including popupView
view's subView's alpha value does not change, including popupView
popupView is a subView of view,view's alpha value affects its subviews's alpha.
view.addSubview(popupView)
Your current structure:
RootView->Subviews //Changing RootView alpha effects Subviews.
The solution is that the popupView is not a subview of view , which you show color changing with
Need a container view to separate from popupView
// backgroundColorChangeContainerView add other views ( tableView ... )
view.addSubview(backgroundColorChangeContainerView)
view.addSubview(popupView)
Make your Popup view full screen ..which have background and have subview as popUp over that background ...so you dont need to change anything once you show or hide popup and its easy to implement... Full screen UIView having popUp UIView over it
You should have a view called it MainPopUpView ... in this view you will add two UIViews ...
Background View ... with black color with alpha 0.3
PopUpUI View ... which shows actual popup
-> backGround UIView full screen (with alpha 0.3)
MainPopUpView
-> popUpView short & centre and shows actual content
So MainPopUpView have two views ...
here is the hierarchy
other way to achieve like your final image.. custom pop controller avoids this situation
put your popupView in a custom viewController, then
#IBAction func addAddressBtn(_ sender: Any) {
present(PopupViewController(nibName: "PopupViewController", bundle: nil), animated: true) {}
}
I have a Vertical UIStackView(S1) with UIView (V1) and UILabel(L1). UIView contains Horizontal StackView(S2) with an ImageView and Vertical StackViews(S3) to show label one below the other.
Now when I hide top view i.e V1, then my Label occupied full height which is expected. But I want stack view to compress to show only Label(L1) content. But in my case it is not reducing the width.
Here are my ViewTree and Snapshots when launched and when V1 is hidden.
When you set .isHidden = true on a stack view's arranged subview, the stack view will remove the space it was taking up... but only in the .axis direction.
So your Stack View still allocates the width of Top PINK View.
To remove the height and width of Top PINK View, you'll need to remove it from the stack view... not just hide it.
Try it like this - tapping the button will toggle between hidden and showing:
#IBOutlet var mainStackView: UIStackView!
#IBAction func showHide(_ sender: Any) {
if !topPINKView.isHidden {
topPINKView.isHidden = true
topPINKView.removeFromSuperview()
} else {
mainStackView.insertArrangedSubview(topPINKView, at: 0)
topPINKView.isHidden = false
}
}
Note: be sure to connect your stack view to the #IBOutlet var mainStackView: UIStackView!
Edit
You could even reduce that to:
#IBAction func showHide(_ sender: Any) {
if topPINKView.superview != nil {
topPINKView.removeFromSuperview()
} else {
mainStackView.insertArrangedSubview(topPINKView, at: 0)
}
}
I am developing an app on XCode using Swift 2 and have run into an error with my UI. I have placed a UILabel, UITextView, and UITextField on the View Controller of interest.
Before inputting the UITextField, everything worked fine. I inserted the text field and assigned the proper constraints and when simulating the app, clicking on the text field will cause all of the UI elements to disappear or the alpha to go instantaneously to zero (I'm not sure which of the two is actually happening).
I'm not receiving any feedback via the console and the app itself does not crash.
Here is my code:
//Declare the following UI objects to be manipulated
#IBOutlet weak var labelOneTitle: UILabel!
#IBOutlet weak var textDescription: UITextView!
#IBOutlet weak var fieldInput: UITextField!
override func viewDidLayoutSubviews() {
//Set UI object alphas to zero prior to loading the view
labelOneTitle.alpha = 0
textDescription.alpha = 0
fieldInput.alpha = 0
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
UIView.animateWithDuration(2) { () -> Void in
self.labelOneTitle.alpha = 1
}
UIView.animateWithDuration(3) { () -> Void in
self.textDescription.alpha = 1
}
UIView.animateWithDuration(3) { () -> Void in
self.fieldInput.alpha = 1
}
}
It might be the UIViewController.viewDidLayoutSubviews rechecking all the constraints and reconfiguring the view as per the the class reference:
When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after
the view lays out its subviews. The default implementation of this
method does nothing.
When you click on the UITextField the keyboard appears (Probably) and thus recalling the method to set the alpha of all your UI Elements to 0.
If you'd move setting the alpha's to 0 to the viewDidLoad() than the problem might go away.
I'm trying to toggle between 2 tableviews based on a segment control. To do this, I've tested 2 implementations:
Option A: Have 2 container views stacked in a storyboard (same constraints), then toggle the container views based on the segment control.
Option B: Have 1 container view setup, add and remove the views programmatically.
The problem with both implementations is that the first view to display is always aligned correctly (no matter if its view A or view B), the second one is wrong aligned! rrrr
Why is the second view to display wrong aligned!?!?
Here is the code for example A:
class MainViewController: UIViewController {
#IBOutlet weak var viewOne: UIView!
#IBOutlet weak var viewTwo: UIView!
#IBOutlet weak var segmentContrl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
showView(0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showView(index:Int){
switch index
{
case 0:
viewOne.hidden = false
viewTwo.hidden = true
break;
case 1:
viewOne.hidden = true
viewTwo.hidden = false
break;
default:
break;
}
}
#IBAction func onSegmentChanged(sender: AnyObject) {
print("onSegmentChanged \( segmentContrl.selectedSegmentIndex)")
showView(segmentContrl.selectedSegmentIndex);
}
Storyboard, the container views are stacked:
The container views constraints, both have the same!
Fixed the problem by removing "Adjust scroll view insets" in all 3 view controllers shown above.
Then I repositioned the container views to top and bottom align then to the layout guides.
WORKS! :P