iOS Charts, Remove extra space created by xAxis Labels at bottom - ios

I have a vertical normal looking bar chart created with iOS Charts. My issue is that for some reason whenever I run the program, the chart does not fill up the view it is in from top to bottom. I am specifically trying to fix the fact that there is a small space between the bottom of a bar and the bottom of the UIView. How can I remove this space so the bottom of the bar aligns perfectly with the bottom of the UIView? (Need No gap)
Here is my code:
barChartView.xAxis.labelPosition = .top
barChartView.xAxis.drawGridLinesEnabled = false
barChartView.xAxis.drawAxisLineEnabled = false
barChartView.leftAxis.drawGridLinesEnabled = false
barChartView.leftAxis.drawAxisLineEnabled = false
barChartView.rightAxis.drawAxisLineEnabled = false
barChartView.rightAxis.drawGridLinesEnabled = false
barChartView.rightAxis.enabled = false
barChartView.leftAxis.enabled = false
barChartView.xAxis.labelTextColor = UIColor.white
barChartView.data?.setDrawValues(false)
barChartView.xAxis.labelFont = UIFont(name: "HelveticaNeue-Bold", size: 12.0)!
barChartView.legend.enabled = false
barChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values:xValues)
barChartView.xAxis.granularity = 1
barChartView.animate(xAxisDuration: 1.0, yAxisDuration: 1.0, easingOption: .easeInQuint)
barChartView.isUserInteractionEnabled = false
barChartView.barData?.barWidth = 0.65
Here is a pic of the issue...I need to get rid of that gap between the bottom of the bar and the red space. The UIView that the graph is in (Not the same as the red view) reaches down to the border of the red view.

Try this:
barChartView.minOffset = 0.0
If this is not worked, add this too.
barChartView.extraBottomOffset = -10

I can't discover special options in BarChartView to resolve your problem. But I can suggest another way. You can embed your barChartView into another view and set a vertical space constraint from between bottom of your barChartView to bottom a superview with a negative value, also set clipToBounds = true for the superview. So the bottom of barChartView will be below the bottom of superview and will be cut.
View after embedding (yellow background - superview, white - barChartView).
View after bottom constraint was set to -14.

Was able to solve my problem by adjusting the bottom constraint programmatically based on the size of the phone's display.
I added this code into viewWillAppear and I will continue to do this for the rest of the iPhone sizes and not just the Xs:
if UIScreen.main.bounds.height == 896{
print("Xs Max")
bottomConstraintGraph.constant = -31.5 // bottomConstraintGraph is the constraint connected from the bottom of the graph's view to the superview and changed here through an outlet connection.
}

Related

Centering between two anchors

I want to set a centerYAnchor between two anchors. Similar to this:
centeredLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
However, I don't want it to be centered relative to the screen. I want it to be right in between two other anchors on the screen. Like if I have a toolbar at the top like this:
toolbar.topAnchor.constraint(equalTo: view.topAnchor),
Then I have a button at the bottom like this:
button.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: -20)
is there a way I can center centeredLabel's y constraint to be right between the bottomanchor of toolbar and the top anchor of button?
is there a way I can center centeredLabel's y constraint to be right between the bottomanchor of toolbar and the top anchor of button?
Yes, there is. The simple way is to use a transparent spacer view whose top is anchored to the upper anchor and whose bottom is anchored to the lower anchor. Now you center-anchor your label to the center of the spacer view.
However, although that is simple, it is not the best way. The best way is to create, instead of a transparent spacer view, a custom UILayoutGuide. Unfortunately this can be done only in code, not in the storyboard (whereas the spacer view and label can be configured entirely in the storyboard). But it has the advantage that it doesn't burden the rendering tree with an additional view.
Here's your situation, more or less, using a button as the upper view and a button as the lower view. The label is centered vertically between them:
Here's the code that generated that situation. b1 and b2 are the buttons (and it doesn't matter how they are created and positioned):
let g = UILayoutGuide()
self.view.addLayoutGuide(g)
g.topAnchor.constraint(equalTo: b1.bottomAnchor).isActive = true
g.bottomAnchor.constraint(equalTo: b2.topAnchor).isActive = true
g.leadingAnchor.constraint(equalTo:b1.leadingAnchor).isActive = true
g.trailingAnchor.constraint(equalTo:b1.trailingAnchor).isActive = true
let lab = UILabel()
lab.text = "Label"
lab.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(lab)
lab.leadingAnchor.constraint(equalTo:g.leadingAnchor).isActive = true
lab.centerYAnchor.constraint(equalTo:g.centerYAnchor).isActive = true
Although #matt's solution works, here is a simpler one that uses autolayout without the need to create a UILayoutGuide.
iOS 10 introduced a simple way of doing this through NSLayoutXAxisAnchor.anchorWithOffset(to:) and NSLayoutYAxisAnchor.anchorWithOffset(to:).
Here are convenience methods which wrap up this logic.
For X axis centering
extension NSLayoutXAxisAnchor {
func constraint(between anchor1: NSLayoutXAxisAnchor, and anchor2: NSLayoutXAxisAnchor) -> NSLayoutConstraint {
let anchor1Constraint = anchor1.anchorWithOffset(to: self)
let anchor2Constraint = anchorWithOffset(to: anchor2)
return anchor1Constraint.constraint(equalTo: anchor2Constraint)
}
}
For Y axis centering
extension NSLayoutYAxisAnchor {
func constraint(between anchor1: NSLayoutYAxisAnchor, and anchor2: NSLayoutYAxisAnchor) -> NSLayoutConstraint {
let anchor1Constraint = anchor1.anchorWithOffset(to: self)
let anchor2Constraint = anchorWithOffset(to: anchor2)
return anchor1Constraint.constraint(equalTo: anchor2Constraint)
}
}
To do what you need you can call:
centeredLabel.centerYAnchor.constraint(between: toolbar.bottomAnchor, and: button.topAnchor)

Menu bar and images/buttons don´t align properly

I´m having trouble with NSLayoutConstraints. I wanted to create a custom menu bar right underneath the header with some images(or buttons) for the navigation but I have no idea why the images do not properly align in the middle and I guess the menu bar does not align underneath the header.. I tried to fix it with changing some values but I´m clueless at this point.
private func setUpMenuBar() {
view.addSubview(menuBar)
let viewWidth = view.viewWidth
menuBar.translatesAutoresizingMaskIntoConstraints = false
menuBar.centerXAnchor.constraint(equalTo:
view.centerXAnchor).isActive = true
menuBar.centerYAnchor.constraint(equalTo:
view.topAnchor).isActive = true
menuBar.heightAnchor.constraint(equalToConstant: 100).isActive
= true
menuBar.widthAnchor.constraint(equalToConstant:
viewWidth).isActive = true
}
Your menuBar is currently the same width as your view so this is why your buttons are evenly spaced across the view. Try making your menuBar half of the view width so that your buttons are closer together (you can experiment here):
menuBar.widthAnchor.constraint(equalToConstant: viewWidth / 2).isActive = true
If you also want a bit of space between the bar, try adding a topConstraint (you can experiment with changing the 30):
menuBar.topAnchor.constraint(equalToConstant: 30).isActive = true

Swift UIStackView utilize full width of UIStackView

In above screen, you can see I am using a UIStackView to fill radio buttons vertically. problem is my radio buttons not utilising the full width of UIStackView when I use stackV.alignment = .leading it shows label as "dis..lified" instead of disqualified.
UISTackView Code
let ratingStackView : UIStackView = {
let stackV = UIStackView()
stackV.translatesAutoresizingMaskIntoConstraints = false
stackV.backgroundColor = UIColor.yellow
stackV.axis = .vertical
stackV.distribution = .fillEqually
stackV.alignment = .leading
return stackV
}()
Layout of UIStackView
func setupView(){
view.addSubview(ratingStackView)
ratingStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
ratingStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,constant: 8).isActive = true
ratingStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
ratingStackView.heightAnchor.constraint(equalToConstant: 200).isActive = true
//Add radio buttons to stackview
for ratingButton in ratingRadioButtons{
ratingStackView.addArrangedSubview(ratingButton)
}
}
what property I need to set to utilize full width can you please tell I am new to the Swift for radio buttons. I am using DLRadioButton.
To get this working, you need to make following changes in the layout:
1. Set UIStackView's alignment property to fill, i.e.
stackV.alignment = .fill
2. Set UIButton's Horizontal Alignment to left wherever you are creating the RadioButton either in .xib file or through code.
In .xib, you can find the property in interface here:
if you are creating the button using code, use the following line of code:
ratingButton.contentHorizontalAlignment = .left
Let me know if you still face the issue. Happy coding..🙂
Leave alignment with its default value, i.e. .fill – this stretches arranged views in a direction perpendicular to the stack’s axis.
Actually, I suspect that if you are using .leading alignment and do not specify widths of nested controls you are getting auto layout warnings during runtime (could be checked in Visual Debugger in Xcode).
Try proportional distribution.
One more thing to try...Reduce the content hugging priority of the labels.

Swift how to set UIView's height constraint based on it's content

I have a UIView in my swift code
let profile_inf_wrapper: UIView = {
let v = UIView()
v.backgroundColor = .red
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
profile_inf_wrapper.topAnchor.constraint(equalTo: view.topAnchor, constant:64).isActive = true
profile_inf_wrapper.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
profile_inf_wrapper.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
profile_inf_wrapper.heightAnchor.constraint(equalToConstant: view.frame.height/4).isActive = true
backgroundImageView.topAnchor.constraint(equalTo: profile_inf_wrapper.topAnchor).isActive = true
backgroundImageView.leftAnchor.constraint(equalTo: profile_inf_wrapper.leftAnchor).isActive = true
backgroundImageView.rightAnchor.constraint(equalTo: profile_inf_wrapper.rightAnchor).isActive = true
backgroundImageView.bottomAnchor.constraint(equalTo: profile_inf_wrapper.bottomAnchor).isActive = true
profileImage.centerYAnchor.constraint(equalTo: profile_inf_wrapper.centerYAnchor).isActive = true
profileImage.leftAnchor.constraint(equalTo: view.leftAnchor, constant:25).isActive = true
profileImage.widthAnchor.constraint(equalToConstant: 110).isActive = true
profileImage.heightAnchor.constraint(equalToConstant: 110).isActive = true
usernameLabel.topAnchor.constraint(equalTo: profile_inf_wrapper.topAnchor, constant:40).isActive = true
usernameLabel.leftAnchor.constraint(equalTo: profileImage.rightAnchor, constant:20).isActive = true
countryIcon.topAnchor.constraint(equalTo: usernameLabel.bottomAnchor, constant:10).isActive = true
countryIcon.leftAnchor.constraint(equalTo: profileImage.rightAnchor, constant:20).isActive = true
countryIcon.widthAnchor.constraint(equalToConstant: 25).isActive = true
countryIcon.heightAnchor.constraint(equalToConstant: 25 ).isActive = true
countryName.topAnchor.constraint(equalTo: usernameLabel.bottomAnchor, constant:5).isActive = true
countryName.leftAnchor.constraint(equalTo: countryIcon.rightAnchor, constant:10).isActive = true
countryName.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
All these elements are the subviews of profile_inf_wrapper.Sometimes view.frame.height/4 is too small and i want to be able to resize the UIView based on it's content
There's a property in UIView called intrinsicContentSize. It returns the smallest size that the view would need show all of it's content.
While the default implementation is not very useful because a UIView doesn't have any content on it's own, all of the default subclasses implement it.
A UILabel will return a size that fits the text perfectly, and a UIButton will return a size that fits it's contents plus whatever spacing you've added. You get the gist of it.
You can take advantage of this property by only constraining either width or height of a view, not both. If you constrain the width of a UILabel and add more text, it will grow vertically.
Finally, when you add subviews to a UIView, and you add constraints to both margins of an axis (top and bottom or left and right), as long as there's a "chain" of constraints and views, and the view doesn't have any constraints on the size, it will expand to fit.
For example, if you have a view with a label and a button, vertically arranged, if the label is constrained to the top, then constrained to the button, and the button is constrained to the bottom, as long as the container view doesn't have a height constraint, it will expand to fit the two views plus the margins perfectly.
Your goal should always be to use the least amount of constraints to express your design, without removing useful constraints. Make sure you take advantage of the intrinsicContentSize.
For setting the height of uiview dynamically you have to add height/bottom constraint to the view in your problem it might be
profile_inf_wrapper.heightAnchor.constraint(equalToConstant: view.frame.height/4+countryName.frame.size.height).isActive = true
you also need the view size to fit to get actual updated size
like
countryName.sizeToFit()
And then update layout if needed to get all affect
The first thing you want to do is make a reference to the height constraint of profile_inf_wrapper.
var profile_inf_wrapper_height_constraint: NSLayoutConstraint?
I don't know the details of your content, but when the view needs resized, you can check that with a conditional in your viewController,
if contentRequiresResizing {
profile_inf_wrapper_height_constraint.constant = view.frame.width/3
else {
profile_inf_wrapper_height_constraint.constant = view.frame.width/4
}
By referencing constraints, it allows you to support dynamic UI changes easily.
As a side note, I would recommend renaming your UIView variable name so that the reference constraint isn't so long. The Swift 3 API guidelines also support lowerCamelCase, as opposed to underscore naming.

UIScrollView how do you constrain a sub view that acts a container to all the other views?

so as you will see below I have a scrollview and I want to add it the the UIViewControllers root view. When I have it constrained to the top, right, bottom, and left I expect to see the red color take up the whole screen. This obviously works, but I want to add a subview to the scrollview that will wrap all the child views. How would I go about doing that?
I have added the view and I have set the same constraints except this time they are set from the wrapper view to the bounds of the UIScrollView, and the blue background color doesn't show anywhere. Also feel free to point out if this is a bad idea, but I thought I could just have it be constrained to the bottom and it will automatically extend the scrollviews content size as needed. This seems to work when I had all the subviews in the scrollview without a wrapper and the last view would extend the content size.
scrollView = UIScrollView(frame: view.bounds)
scrollView?.showsVerticalScrollIndicator = true
scrollView?.backgroundColor = .red
scrollView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView!)
scrollView?.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView?.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView?.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView?.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//setup wrapper view
let subviewWrapper = UIView()
subviewWrapper.translatesAutoresizingMaskIntoConstraints = false
scrollView?.addSubview(subviewWrapper)
subviewWrapper.backgroundColor = .blue
subviewWrapper.topAnchor.constraint(equalTo: (scrollView?.topAnchor)!).isActive = true
subviewWrapper.leftAnchor.constraint(equalTo: (scrollView?.leftAnchor)!).isActive = true
subviewWrapper.rightAnchor.constraint(equalTo: (scrollView?.rightAnchor)!).isActive = true
subviewWrapper.bottomAnchor.constraint(equalTo: (scrollView?.bottomAnchor)!).isActive = true
Actually this is a very good idea. I always set up my scrollViews this way. I usually call the view contentView, but it is the same idea.
You're almost there. You haven't yet given Auto Layout anything to go on to figure out the size of your subviewWrapper. The constraints you've set so far pin the subviewWrapper to the edges of the scrollView's content area, but this just establishes the fact that as the subviewWrapper grows, the content size of the scrollView will expand. Currently your subviewWrapper has 0 width and 0 height which is why you see no blue.
Below are 3 examples of how you might establish the size of your subviewWrapper.
Note: Each of the following examples is completely independent. Look at each one separately and as you try them, remember to delete the constraints added by the previous example.
Example 1: Make subviewWrapper 1000 x 1000:
Set constraints to make your subviewWrapper 1000 x 1000 and you will see the blue and it will scroll in both directions.
subviewWrapper.widthAnchor.constraint(equalToConstant: 1000).isActive = true
subviewWrapper.heightAnchor.constraint(equalToConstant: 1000).isActive = true
Example 2: Vertical only scrolling with content size 2X of scrollView height:
If you set the width of your subviewWrapper to be equal to the width of the scrollView then it will only scroll vertically. If you set the height of subviewWrapper to 2X the height of scrollView, then your blue area will be twice the height of the scrollView.
subviewWrapper.widthAnchor.constraint(equalTo: scrollView!.widthAnchor, multiplier: 1.0).isActive = true
subviewWrapper.heightAnchor.constraint(equalTo: scrollView!.heightAnchor, multiplier: 2.0).isActive = true
Example 3: Size of subviewWrapper set by its subviews:
You can also establish the size of your subviewWrapper by adding subviews to it that are fully specified in size and connected in a chain from the top of subviewWrapper to the bottom, and from side to side. If you do this, Auto Layout will have enough information to compute the size of your subviewWrapper
In this example, I've added a yellow 600 x 600 square to the subviewWrapper and set it 100 points from each edge. Without having explicitly set a size for subviewWrapper, Auto Layout can figure out that it is 800 x 800.
let yellowSquare = UIView()
yellowSquare.translatesAutoresizingMaskIntoConstraints = false
yellowSquare.backgroundColor = .yellow
subviewWrapper.addSubview(yellowSquare)
yellowSquare.widthAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.heightAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.topAnchor.constraint(equalTo: subviewWrapper.topAnchor, constant: 100).isActive = true
yellowSquare.leadingAnchor.constraint(equalTo: subviewWrapper.leadingAnchor, constant: 100).isActive = true
yellowSquare.trailingAnchor.constraint(equalTo: subviewWrapper.trailingAnchor, constant: -100).isActive = true
yellowSquare.bottomAnchor.constraint(equalTo: subviewWrapper.bottomAnchor, constant: -100).isActive = true

Resources