Swift 4 - Getting half the width of a button - swift

I'm trying to make a fully circular button. I have created it in code and setup the constraints like this:
addButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2).isActive = true
addButton.heightAnchor.constraint(equalTo: addButton.widthAnchor).isActive = true
addButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30).isActive = true
addButton.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -30).isActive = true
I then try and set the corner radius of the button by using this:
addButton.layer.cornerRadius =
addButton.layer.masksToBounds = true
But I don't know what to put in the corner radius. I have tried frame.width / 2 but the button remains square. I need to get the value of the width constraint but I can't convert it into a CGFloat.
Any suggestions on how to do this?
Thanks

Inside viewDidLayoutSubviews put
addButton.layer.cornerRadius = self.view.frame.width * 0.1

You can either use the above answer posted by #Sh_Khan (url: https://stackoverflow.com/a/51808795/7425588) or go with the following:
addButton.layer.cornerRadius = addButton.widthAnchor.constant / 2.0
addButton.clipToBounds = true
Remember to make an UIObject complete circular, make it's height and width equal or aspect ratio to 1:1. Set clip to bounds to true and give corner radius as half of width or height.

Related

how to get the screen size until the bottom of the tabbar

I'm working on an custom tab bar button stuff and want to know how can I move up the button using the view's height or something programatically.
I have this plus button and I want to move up 10pt from the bottom.
let newButton = UIButton(frame: CGRect(x: 0, y: 0, width: 65, height: 65))
newButton.imageView?.contentMode = .scaleAspectFit
newButton.contentHorizontalAlignment = .fill
newButton.contentVerticalAlignment = .fill
var newButtonFrame = newEventButton.frame
newButtonFrame.origin.y = view.bounds.height - 65 - 10
So the code above works when an iPhone doesn't have a home button notch. Like get the height of the view and subtract button height and 10.
But, obviously, when there is a notch, it becomes something like the image below.
I want to move up the button from the bottom of the tabbar, so how can I get the height of the screen till the bottom of the tabbar, which works on both with/without home notch?
While #wonder is completly correct in his answer, there are still some flaws.
First you shouldn't use frame to determine views layout like this.
Second there are no need to reach into the UIApplication singleton of windows for this. As it's located in the parents view. Either in safeAreaInsets or safeAreaLayoutGuide.
The layout guide is for anchor points, which is way more sustainable than frames.
Here you have example code of how it can look:
let newButton = UIButton()
newButton.imageView?.contentMode = .scaleAspectFit
newButton.contentHorizontalAlignment = .fill
newButton.contentVerticalAlignment = .fill
NSLayoutConstraint.activate([
newButton.heightAnchor.constraint(equalTo: 65),
newButton.widthAnchor.constraint(equalTo: 65),
newButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
newButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
])
For button origin use safe are insets, ıf device has notch safeAreaBottom will be like 44 , if not it will be 0:
let safeAreaBottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom
Then use it :
newButtonFrame.origin.y = view.bounds.height - safeAreaBottom - 65 - 10

satisfying two auto layout constraints programmatically - Swift

I want to set a UIButton with autoLayout constraints:
Basically I want the height to be the multiplier of the container view height:
button.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.43).isActive = true
and if the height doesn't reach the constant of 44 I want to set it to 44, I added this:
button.heightAnchor(greaterThanOrEqualToConstant: 44).isActive = true
Obviously programmatically setting 2 constraints like this causes a conflict, is there a way for programmatically accomplishing these 2 prerequisites without causing a warning?
You will need to set lower priority for the "ratio" constraint
let constraint = button.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.43)
constraint.priority = .defaultHigh
constraint.isActive = true
if the engine still have troubles with figuring out the layout, you can try different priority value.
UILayoutPriority documentation
You either set a low priority of
let con = button.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.43)
con.layoutPriority //////////////
con.isActive = true
or calculate it yourself mathematically and decide with if statement

Setting UIImageView Width, Height, Aspect Ratio & Constraints X, Y

I've set a UIImageView in my view controller and assigned constraints x & Y which works I'm trying to set the image width, height & aspect ratio but it doesn't seem to be working.
I've tried many answers already on here but can seem to get it working.
import UIKit
class GuestLoginViewController: UIViewController {
let headerImage = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
setHeaderImage()
}
func setHeaderImage() {
view.addSubview(headerImage)
headerImage.frame = CGRect(x: 0, y: 0, width: super.view.frame.width, height: 95)
headerImage.translatesAutoresizingMaskIntoConstraints = false
headerImage.mask?.contentMode = UIView.ContentMode.redraw
headerImage.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
headerImage.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerImage.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
headerImage.image = UIImage(named: "header")
view.sendSubviewToBack(headerImage)
}
}
As soon as you set headerImage.translatesAutoresizingMaskIntoConstraints = false, the frame is ignored. You need to set some constraint to establish the height of your UIImageView. Unfortunately, the image contents does not affect the height of the UIImageView.
Set either:
an absolute height constraint
an offset from the bottom of the superView
height relative to the width with a multiplier (an "aspect ratio constraint")
Based on my comment, you turned off headerImage.translatesAutoresizingMaskIntoConstraints = false and it worked.
This gives you extra constraints (your 3 plus the 4 that are generated from the frame), but luckily they aren't conflicting.
Instead, I would suggest you leave the translatesAutoresizingMaskIntoConstraints set to false and set a constraint for the height:
headerImage.heightAnchor.constraint(equalToConstant: 95).isActive = true

Swift: Retain floating point decimal in autolayout

this is a follow on question from my post here. I am attempting to fix my heightConstraint of imageView dynamically depending on the size of my image, and then add a drawing layer over it. However, I noticed that the imageView size loses precision due to the division of floating point and this resulted in a blurred image when I start to draw on my image, ie the placement of the bitmap is not exactly at the position overlaying my original image.
My code as such, with the print statements to observe the decimal places.
func setupViews() {
view.backgroundColor = .black
view.addSubview(canvasImageView)
canvasImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
canvasImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
canvasImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
let aspectRatio = getImageAspectRatio(image: image)
let screenWidth = UIScreen.main.bounds.width
let height = CGFloat(1.0) / CGFloat(aspectRatio) * CGFloat(screenWidth)
canvasImageView.heightAnchor.constraint(equalToConstant: height).isActive = true
print("ImageSize", image.size.height)
print("Aspect Ratio image at start:", aspectRatio)
print("Calculated height:", height)
DispatchQueue.main.async {
print("Aspect Ratio imageView at start:", self.getAspectRatio(frame: self.canvasImageView.frame))
print("ImageViewSize", self.canvasImageView.frame.height)
}
}
My print statements are here:
ImageSize 3000.0 2002.0
Aspect Ratio image at start: 1.4985014985015
Calculated height: 213.546666666667
Aspect Ratio imageView at start: 1.49532710280374
ImageViewSize 320.0 214.0
As you can see, my attempt to do the heightAnchor.constraint(equalToConstant: height) actually loses the precision and resulting in a lost of resolution. Is there a way to eliminate this? An image of the outcome is here.

Programmatically adding constraints breaks auto layout constraints

Edit 1
Hello, This is my first time using code to add constraints. I normally just use interface builder. I am trying to add a vertical UISlider, before I thought that mixing visually added constraints was interfering with coded constraints. I have now updated my code and therefore this question to create constrains using ONLY code in this particular view container.
What I have done is I created a view directly underneath another view. I created 3 small views inside that that match the width of the textfields in the view above and then spaced them out the same as how the textfields are spaced out.
For testing purposes only I gave these 3 small views a color to see if it worked, and it does.
When I actually finish the app those red, green, and blue views will be clear. The only reason I wanted them was so when I create the sliders I can constrain each one of them to the center of the view...which is how the labels above the textfields are constrained.
Here is the code for this
which works
// Mark: Hidden View
let leftHiddenView = UIView()
let centerHiddenView = UIView()
let rightHiddenView = UIView()
let hiddenViews = [leftHiddenView, centerHiddenView, rightHiddenView]
for views in hiddenViews {
views.translatesAutoresizingMaskIntoConstraints = false
sliderContainer.addSubview(views)
views.backgroundColor = .white
let widthConstraint = views.widthAnchor.constraint(equalToConstant: 35)
let heightConstraint = views.heightAnchor.constraint(equalToConstant: 5)
NSLayoutConstraint.activate([widthConstraint, heightConstraint])
}
let centerViewHorizontalConstraint = centerHiddenView.centerXAnchor.constraint(equalTo: sliderContainer.centerXAnchor)
let centerViewTopConstraint = centerHiddenView.topAnchor.constraint(equalTo: sliderContainer.topAnchor, constant: 50)
NSLayoutConstraint.activate([centerViewHorizontalConstraint, centerViewTopConstraint])
let leftViewVerticalCenterConstraint = leftHiddenView.centerYAnchor.constraint(equalTo: centerHiddenView.centerYAnchor, constant: 0)
let leftViewTrailingConstraint = leftHiddenView.trailingAnchor.constraint(equalTo: centerHiddenView.leadingAnchor, constant: -60)
NSLayoutConstraint.activate([leftViewVerticalCenterConstraint, leftViewTrailingConstraint])
let rightViewVerticalCenterConstraint = rightHiddenView.centerYAnchor.constraint(equalTo: centerHiddenView.centerYAnchor, constant: 0)
let rightViewTrailingConstraint = rightHiddenView.leadingAnchor.constraint(equalTo: centerHiddenView.trailingAnchor, constant: 60)
NSLayoutConstraint.activate([rightViewVerticalCenterConstraint, rightViewTrailingConstraint])
Now, I started to add a UISlider as vertical. And the exact same thing that happened before happened now.
As you can see everything breaks.
Here is the code thus far on that
// Mark: Slider View
let leftSlider = UISlider()
let centerSlider = UISlider()
let rightSlider = UISlider()
let colorSliders = [leftSlider, centerSlider, rightSlider]
for slider in colorSliders {
slider.translatesAutoresizingMaskIntoConstraints = false
sliderContainer.addSubview(slider)
let w = sliderContainer.bounds.width
slider.bounds.size.width = w
slider.center = CGPoint(x: w/2, y: w/2)
slider.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_2))
slider.value = 0
slider.minimumValue = 0
slider.maximumValue = 255
let sliderTopConstraint = slider.topAnchor.constraint(equalTo: centerHiddenView.bottomAnchor, constant: 5)
let sliderBottomConstraint = slider.bottomAnchor.constraint(equalTo: sliderContainer.bottomAnchor, constant: 5)
NSLayoutConstraint.activate([sliderTopConstraint, sliderBottomConstraint])
slider.backgroundColor = .purple
}
let centerSliderHorizontalConstraints = centerSlider.centerXAnchor.constraint(equalTo: sliderContainer.centerXAnchor)
NSLayoutConstraint.activate([centerSliderHorizontalConstraints])
Don't ever misc the Design time constraints with adding Runtime constraints. Either add all constraints at design time or all constraints at runtime only. Else you will be in MESS. Make this as a good practice.
In case you need to change the frame, just change the constant property of the constraints and add all the required constraint at design time.
There will be very rare times when you need to add runtime constraints. (I am saying this because I always design in that way only. And that helps me a lot.) Design your screen in such a ways that even if you need to add 2 controls for dynamic UI changes, then keep 2 controls and do show hide with that controls. In can you need some kind of Animation with your controls, you don't need to change design time constraints.
I know this does not answer your question directly, but hope you will get the understanding of how to use constraints.
From your screenshot, I am not able understand exactly what is your UI looks like. Can you give little bit more idea of how your UI looks like? so that I can suggest some idea of how to give constraints...
Well, it turns out that the problem was actually quite easy to solve from the beginning. I only overlooked it because of being intimidated by the vertical UISlider. Since I did not give the above container a fixed height when I added the container below it and ran the app, the containers equally filled the space and the contents inside messed up accordingly. I simply gave the top container with just the labels and textfield a fixed height of 61 and its now much closer to being done. Sorry