IOS how to completely remove a View - ios

I want to hide a view completely. I am new to ios. But I have great working experience in android.
So in android We can set visibility to gone. and it completely removes the view from layout. Same I want to do in IOS. here is the layout/design example
View 1
View 2 ( it is the one I want to hide and show)
View 3
now when I want to hide the view 2 I want the space of view 2 also vanish from screen and View 1 and View 3 must stick together . and when view 2 is set to visible then it must display in sequence. i.e View 1,2,3
Right now what I am doing is setting view2.ishidden = true but its not working in the way I want.
Please tell me what is equivalent to view.gone of android in IOS. ???

There are a few ways in achieving this but what you are missing here is some key information on how your layout is being set.
I will assume that the 3 views you are having are vertically aligned, are one next to each other and have equal width.
Programmatically what we are looking at from horizontal perspective this is done:
let firstLeading = NSLayoutConstraint(item: view1, attribute: .leading, relatedBy: .equal, toItem: parent, attribute: .leading, multiplier: 1.0, constant: 0.0)
let betweenSecondAndFirst = NSLayoutConstraint(item: view2, attribute: .leading, relatedBy: .equal, toItem: view1, attribute: .trailing, multiplier: 1.0, constant: 0.0)
let secondEqualWidth = NSLayoutConstraint(item: view2, attribute: .width, relatedBy: .equal, toItem: view1, attribute: .width, multiplier: 1.0, constant: 0.0)
let betweenThirdAndSecond = NSLayoutConstraint(item: view3, attribute: .leading, relatedBy: .equal, toItem: view2, attribute: .trailing, multiplier: 1.0, constant: 0.0)
let lastEqualWidth = NSLayoutConstraint(item: view3, attribute: .width, relatedBy: .equal, toItem: view1, attribute: .width, multiplier: 1.0, constant: 0.0) // Note to view1 to make things easier
let lastTrailing = NSLayoutConstraint(item: view3, attribute: .trailing, relatedBy: .equal, toItem: parent, attribute: .trailing, multiplier: 1.0, constant: 0.0)
And when the second is being skipped we may simply disable betweenThirdAndSecond and add betweenThirdAndFirst as:
let betweenThirdAndFirst = NSLayoutConstraint(item: view3, attribute: .leading, relatedBy: .equal, toItem: view1, attribute: .trailing, multiplier: 1.0, constant: 0.0)
You can play with properties on constraints to enable or disable them. Or you can simply use priorities and toggle them for instance from 900 to 100. You can actually setup all of these constraints in storyboard and then drag the 2 as outlets into your code. Then simply have:
func setMiddleViewShown(_ shown: Bool) {
UIView.animate(withDuration: 0.3) {
self.betweenThirdAndSecond.priority = UILayoutPriority(rawValue: shown ? 900.0 : 100.0)
self.betweenThirdAndFirst.priority = UILayoutPriority(rawValue: shown ? 100.0 : 900.0)
self.view2.alpha = shown ? 1.0 : 0.0
parent.layoutIfNeeded() // parent is most likely "self.view"
}
}
This way is probably best from what you can control (mostly animations). But you may as well use UIStackView which has methods to insert or remove views. UICollectionView should work as well. Or you know.. just do it all programmatically, ignore constraints and simply set frame for each of the views.
A minimum I can think of in using UIStackView is the following and it works:
class ViewController: UIViewController {
let views: [UIView] = [
{ let view = UIView(); view.backgroundColor = .red; return view; }(),
{ let view = UIView(); view.backgroundColor = .green; return view; }(),
{ let view = UIView(); view.backgroundColor = .blue; return view; }()
]
lazy var stackView: UIStackView = {
let stackView = UIStackView(frame: CGRect(x: 50.0, y: 100.0, width: 300.0, height: 200.0))
self.view.addSubview(stackView)
stackView.backgroundColor = .black
stackView.distribution = .fillEqually
return stackView
}()
override func viewDidLoad() {
super.viewDidLoad()
stackView.addArrangedSubview(views[0])
stackView.addArrangedSubview(views[1])
stackView.addArrangedSubview(views[2])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
UIView.animate(withDuration: 0.3) {
if self.stackView.arrangedSubviews.count == 3 {
self.views[1].alpha = 0.0
self.stackView.removeArrangedSubview(self.views[1])
} else {
self.views[1].alpha = 1.0
self.stackView.insertArrangedSubview(self.views[1], at: 1)
}
self.stackView.layoutIfNeeded()
}
}
}

Related

Tab Bar overlap view behind

I can't seem to fix the issue about this where I adjusted the Tab Bar height to 60 from viewWillLayoutSubviews() but the overlay view doesn't seem to acknowledge the adjusted height and follow suit.
Other similar questions I found are not actually alike (see here: iOS 7 Custom TableView Is Under TabBar) as their Tab Bar is translucent, and mine's not.
Here is what I implemented so far:
In my custom UITabBarController:
override func viewWillLayoutSubviews() {
var newTabBarFrame = tabBar.frame
let newTabBarHeight: CGFloat = 60
newTabBarFrame.size.height = newTabBarHeight
newTabBarFrame.origin.y = self.view.frame.size.height - newTabBarHeight
tabBar.frame = newTabBarFrame
}
In one of my tab's UIViewController:
override func viewDidLoad() {
view.addSubview(tableView)
NSLayoutConstraint.activate([
NSLayoutConstraint(item: tableView, attribute: NSLayoutAttribute.leading, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.leading, multiplier: 1, constant: 0),
NSLayoutConstraint( item: tableView, attribute: NSLayoutAttribute.top, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: tableView, attribute: NSLayoutAttribute.trailing, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: tableView, attribute: NSLayoutAttribute.bottom, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
])
}
This is the current result:
You can see the overlay view is partially blocked. This happens on all other tab's overlay view controller
BTW, I already make sure the tableview's translatesAutoresizingMaskIntoConstraints is set to false
You can use a custom UITabBar to do this. Just override sizeThatFits(_:) to use your custom height:
class TabBar: UITabBar {
private let height:CGFloat = 60
override func sizeThatFits(_ size: CGSize) -> CGSize {
var bottomSafeAreaInsets: CGFloat = 0
if #available(iOS 11.0, *) {
bottomSafeAreaInsets = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
}
return CGSize(width: size.width, height: height + bottomSafeAreaInsets)
}
}

Adding leading constraint programmatically crashes app

I'm trying to get my head around how adding constraints programmatically works. So far I have my code like so:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//addViewStandard()
addConstraintsView()
}
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
// I want to mimic a frame set of CGRect(x: 20, y: 50, width: 50, height: 50)
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 20)
someView.translatesAutoresizingMaskIntoConstraints = false
someView.addConstraints([widthConstraint, heightConstraint, leadingConstraint])
view.addSubview(someView)
}
}
Now when I run the app it crashes because of the leading constraint. The error message is "Impossible to set up layout with view hierarchy unprepared for constraint". What am I doing wrong here? Should I be adding the constraints to the object (the blue box on this case) or adding them to its superview?
EDIT:
After code changes I have:
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 20)
someView.addConstraints([widthConstraint, heightConstraint])
view.addConstraints([leadingConstraint])
}
First of all,
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
should come before the constraints phase; you have to apply the constraints AFTER someView is added to its superview.
Also, if you are targeting iOS 9, I'd advise you to use layout anchors like
let widthConstraint = someView.widthAnchor.constraint(equalToConstant: 50.0)
let heightConstraint = someView.heightAnchor.constraint(equalToConstant: 50.0)
let leadingConstraint = someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, leadingConstraint])
This way you don't have to worry about which view to apply the constraints to.
Finally (and to clear up your doubt), if you can't use layout anchors, you should add the leading constraint to the superview, not the view.

How to create Auto layout equal width constraint programmatically in ios swift?

How to give programmatically constraints equal width and equal height with multiple views.I check google but not perfect answer for programmatically equal width and height constraints through auto layout.
my code look like below:
var countNoOfViews:Int = 3
#IBOutlet var viewForRow1: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.specialButtonViewLeft()
}
func specialButtonViewLeft(){
for i in 0..<countNoOfViews{
var customView:UIView!
customView = UIView(frame: CGRect.zero)
customView.translatesAutoresizingMaskIntoConstraints = false
viewForRow1.addSubview(customView)
let widthConstraint = NSLayoutConstraint(item: customView, attribute: .width, relatedBy: .equal,toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 20.0)
let topConstraint = NSLayoutConstraint(item: customView, attribute: .top, relatedBy: .equal,toItem: self.viewForRow1, attribute: .top, multiplier: 1.0, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: customView, attribute: .bottom, relatedBy: .equal,toItem: self.viewForRow1, attribute: .bottom, multiplier: 1.0, constant: 0)
let leadingConstraint = NSLayoutConstraint(item: customView, attribute: .leading, relatedBy: .equal, toItem: self.viewForRow1, attribute: .leading, multiplier: 1, constant: customLeadingSpaceLeft)
customLeadingSpaceLeft = customLeadingSpaceLeft + customViewWidth
arrayLeftBtnConstraints.append(widthConstraint)
if i == 0{
customView.backgroundColor = UIColor.red
}else if i == 1{
customView.backgroundColor = UIColor.green
}else if i == 2{
leftViewVal = customLeadingSpaceLeft
customView.backgroundColor = UIColor.black
}
customView.alpha = 0.50
viewForRow1.addConstraints([widthConstraint, leadingConstraint,topConstraint,bottomConstraint])
}
}
I want to add equal width constraint programmatically.
You have three possible ways to do that:
1) By using anchors:
view.widthAnchor.constraint(equalTo: otherView.widthAnchor, multiplier: 1.0).isActive = true
2) Visual format:
simple example - "H:|[otherView]-[view(==otherView)]|"
3) "Old school" constraints:
NSLayoutConstraint(item: #view, attribute: .width, relatedBy: .equal, toItem: #otherView, attribute: .width, multiplier: 1.0, constant: 0.0).isActive = true
hope it will help somebody.
Try this:
let widthConstraint = NSLayoutConstraint(item: customView, attribute: .width, relatedBy: .equal, toItem: viewForRow1, attribute: .width, multiplier: 0.25, constant: 0.0)
multiplier: 0.25, denotes that customView's width will be 1/4th of the parent view, viewForRow1.

Move a View into Another view on button click in Swift iOS

I am stumbled upon an issue in an application that i am making. I need to place one view into another view programmatically on button click.
Now i need to move View 1 to the centre of View 2 on a button click with an animation. I tried to reposition the View1 to View 2 but i am not able to do it properly.
This is the Final result that i am trying to achieve.
CODE FOR CREATING THE RED VIEW
My.cellSnapshot = snapshopOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
ingredientsTableView.addSubview(My.cellSnapshot!)
func snapshopOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
let cellSnapshot : UIView = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot
}
Please help me in solving the problem.
Thanks in advance
You can move the view for 0.5 seconds.
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
redView.center = greenView.center
}, completion: nil)
i created a sample project and set up a target view as well as a button to start the animation in storyboard like this:
then in code i added the view to move and the button target code like this:
var sourceView: UIView!
#IBOutlet weak var destinationView: UIView!
var sourceViewPositioningConstraints = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
sourceView = UIView()
sourceView?.backgroundColor = UIColor.redColor()
sourceView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(sourceView)
// size constraints
NSLayoutConstraint(item: sourceView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 0.25, constant: 0).active = true
NSLayoutConstraint(item: sourceView, attribute: .Width, relatedBy: .Equal, toItem: sourceView, attribute: .Height, multiplier: 16/9, constant: 0).active = true
// positioning constraints
sourceViewPositioningConstraints += [NSLayoutConstraint(item: sourceView, attribute: .Top, relatedBy: .Equal, toItem: topLayoutGuide, attribute: .BottomMargin, multiplier: 1, constant: 0)]
sourceViewPositioningConstraints += [NSLayoutConstraint(item: sourceView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0)]
NSLayoutConstraint.activateConstraints(sourceViewPositioningConstraints)
}
#IBAction func move(sender: UIButton) {
// deactivate current positioning constraints
NSLayoutConstraint.deactivateConstraints(sourceViewPositioningConstraints)
sourceViewPositioningConstraints.removeAll()
// add new positioning constraints
sourceViewPositioningConstraints += [NSLayoutConstraint(item: sourceView, attribute: .CenterX, relatedBy: .Equal, toItem: destinationView, attribute: .CenterX, multiplier: 1, constant: 0)]
sourceViewPositioningConstraints += [NSLayoutConstraint(item: sourceView, attribute: .CenterY, relatedBy: .Equal, toItem: destinationView, attribute: .CenterY, multiplier: 1, constant: 0)]
NSLayoutConstraint.activateConstraints(sourceViewPositioningConstraints)
// animate constraint changes
UIView.animateWithDuration(1) {
self.view.layoutIfNeeded()
}
}
if you are not using autolayout for your movable view you can simply use something like this:
override func viewDidLoad() {
super.viewDidLoad()
sourceView = UIView()
sourceView?.backgroundColor = UIColor.redColor()
sourceView.frame = CGRect(x: 40, y: 40, width: 100, height: 100)
view.addSubview(sourceView)
}
#IBAction func move(sender: UIButton) {
// animate constraint changes
UIView.animateWithDuration(1) {
self.sourceView.center = self.destinationView.center
}
}

Adjust ScrollView with multiple SubViews

I have to dynamically create some UILabel and UITextViews according to some Data ~ around 20 - all of them with dynamic height/lines of Text for an IOS App in Swift.
Since the screen is not large enough I am adding the Views to a ScrollView, but unfortunately the contentsize property of my ScrollView seems not to receive the proper values.
I'm debugging since a couple of hours and tried different set ups but so far none of them worked out.
The sizing is done in a custom method refreshUI() which gets firstly called in viewDidLoad(). The ViewController simply contains one centred Heading Label and the ScrollView which fills the rest of the space (pinned to Top, Left, Right, Bottom with 8.0).
Then I'm trying to populate my Data in that scrollView as follows:
func refreshUI(){
println("refreshUI()")
questionnaireTitle.text = site.questionnaireName
let questionnaire = site.questionnaire
//removes all SubViews in ScrollView
clearView(scrollView)
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
for questionGroup in questionnaire{
//QUESTIONGROUP HEADING
let questionGroupHeading = UILabel()
questionGroupHeading.setTranslatesAutoresizingMaskIntoConstraints(false)
questionGroupHeading.text = questionGroup.questionsHeading
questionGroupHeading.sizeToFit()
scrollView.addSubview(questionGroupHeading)
viewStack.append(questionGroupHeading)
//QUESTION
for question in questionGroup.questions{
//QUESTION LABEL
let questionLabel = UILabel()
questionLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
questionLabel.text = question.text
questionLabel.numberOfLines = 0
questionLabel.lineBreakMode = .ByWordWrapping
questionLabel.sizeToFit()
scrollView.addSubview(questionLabel)
viewStack.append(questionLabel)
if question.type == "selector"{
//SELECTOR QUESTION
println("selector Question")
for statement in question.statements{
//TODO add Statement + Picker
}
}
else if question.type == "standard"{
//STANDARD QUESTION
println("standard question")
let answerLabel = UITextField()
answerLabel.placeholder = "here goes your answer"
answerLabel.sizeToFit()
answerLabel.backgroundColor = UIColor.whiteColor()
answerLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
scrollView.addSubview(answerLabel)
viewStack.append(answerLabel)
}
}
}
//setUpConstraints
var counter = 0
var height:CGFloat = 0.0
for view in viewStack{
let rightConst = NSLayoutConstraint(item: view, attribute: .Right, relatedBy: .Equal, toItem: scrollView, attribute: .Right, multiplier: 1.0, constant: 0.0)
let leftConst = NSLayoutConstraint(item: view, attribute: .Left, relatedBy: .Equal, toItem: scrollView, attribute: .Left, multiplier: 1.0, constant: 0.0)
let widthConst = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: scrollView.frame.width)
view.addConstraint(widthConst)
scrollView.addConstraint(leftConst)
scrollView.addConstraint(rightConst)
//pin first view to top of scrollView
if counter == 0{
let topConst = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1.0, constant: 0.0)
scrollView.addConstraint(topConst)
}
//pin all other views to the top of the previousView
else{
let topConst = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: viewStack[counter - 1], attribute: .Bottom, multiplier: 1.0, constant: 8.0)
scrollView.addConstraint(topConst)
}
counter++
height += view.bounds.height
}
let contentSize = CGSize(width: scrollView.frame.width, height: height)
scrollView.contentSize = contentSize
scrollView.contentOffset = CGPoint(x: 0, y: 0)
println("refreshUI() done")
}
The ScrollView is not vertically scrollable, although some content is not displayed due to being out of the screen.
But it scrolls horizontally, although I'm setting the width of each view to the width of the SCrollView, which should mean that every SubView is just as big as the size of the ScrollView and thus not vertically scrollable.
If you are using AutoLayout then this tutorial will be useful for implementing scrollview.
When you run refreshUI() inside of viewDidLoad(), the subviews that are being created are using the Interface Builder defaults for the parent views instead of the actual sizes on the device. This is likely why your sizes are not what you expect. If you run refreshUI() inside of viewDidLayoutSubviews() instead, then the subviews will correctly read the widths and heights of the parent view.

Resources