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
Related
I am try in imageView in collection view in center and aspect fit, but not do any things in collection view. please any one help me
imageproduct.center = self.viewImage.center
imageproduct.contentMode = .scaleAspectFit
self.viewImage.addSubview(imageproduct)
Have you tried giving it centerX and centerY constraints after adding into subviews?
imageproduct.translatesAutoresizingMaskIntoConstraints = false
imageproduct.centerXAnchor.constraint(equalTo: self.viewImage.centerXAnchor).isActive = true
imageproduct.centerYAnchor.constraint(equalTo: self.viewImage.centerYAnchor).isActive = true
Also make sure your self.viewImage is also given proper constraint.
How can I shrink the font size to fit in a stackview of 3 items? It's important to note that the font-sizes should be the same across all views in the Stackview.
Please have a look at the issue below. The My Orders tab has a smaller font than the rest of the Stackview.
What would I like to achieve?
Spread all items evenly in the Stackview with the same fontsizes.
What did I already tried?
Setting the following properties on the labels.
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor=0.75
Setting the following properties on the Stackview.
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 16.0
Thanks for any advise.
did you try using fillEqually instead? that should give you equal size for both items.
The way I solved this was to calculate what the percentage of the total width of the stack view each item should take based on the number of characters in the label and using that information to set a width constraint on the labels. Something like this:
let totalCount = labels(into: 0) { $0 += $1.text.count }
labels.forEach { label in
let widthRatio = CGFloat(label.text.count) / CGFloat(totalCount)
label.widthAnchor.constraint(equalTo: label.superview!.widthAnchor, multiplier: widthRatio).isActive = true
}
Is there a way to make the corner radius of a UIView adept to the view it belongs to? I'm not comfortable with the idea of hard-coding corner radius values, because as soon as the width or the height of your view changes (on different screen orientations for example), the corners will look totally different. For example, take a look at WhatsApp's chat window.
As you can see, every message container view has a different width and a different height, but the curve of the corners are all exactly the same. This is what I'm trying to achieve. I want the curves of my corners to be the same on every view, no matter what the size of the view is or what screen the view is displayed on. I've tried setting the corner radius relative to the view's height (view.layer.cornerRadius = view.frame.size.height * 0.25) and I've also tried setting it to the view's width, but this doesn't work. The corners still look weird as soon as they are displayed on a different screen size. Please let me know if there's a certain formula or trick to make the curves look the same on every view/screen size.
Here's the best I can do. I don't know if this will be of help, but hopefully it will give you some ideas.
First the code:
class ViewController: UIViewController {
let cornerRadius:CGFloat = 10
let insetValue:CGFloat = 10
var numberOfViews:Int = 0
var myViews = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
view.translatesAutoresizingMaskIntoConstraints = false
}
override func viewWillLayoutSubviews() {
setNumberOfViews()
createViews()
createViewHierarchy()
addConstraints()
}
func setNumberOfViews() {
var smallerDimension:CGFloat = 0
if view.frame.height < view.frame.width {
smallerDimension = view.frame.height
} else {
smallerDimension = view.frame.width
}
let viewCount = smallerDimension / (insetValue * 2)
numberOfViews = Int(viewCount)
}
func createViews() {
for i in 1...numberOfViews {
switch i % 5 {
case 0:
myViews.append(MyView(UIColor.black, cornerRadius))
case 1:
myViews.append(MyView(UIColor.blue, cornerRadius))
case 2:
myViews.append(MyView(UIColor.red, cornerRadius))
case 3:
myViews.append(MyView(UIColor.yellow, cornerRadius))
case 4:
myViews.append(MyView(UIColor.green, cornerRadius))
default:
break
}
}
}
func createViewHierarchy() {
view.addSubview(myViews[0])
for i in 1...myViews.count-1 {
myViews[i-1].addSubview(myViews[i])
}
}
func addConstraints() {
for view in myViews {
view.topAnchor.constraint(equalTo: (view.superview?.topAnchor)!, constant: insetValue).isActive = true
view.leadingAnchor.constraint(equalTo: (view.superview?.leadingAnchor)!, constant: insetValue).isActive = true
view.trailingAnchor.constraint(equalTo: (view.superview?.trailingAnchor)!, constant: -insetValue).isActive = true
view.bottomAnchor.constraint(equalTo: (view.superview?.bottomAnchor)!, constant: -insetValue).isActive = true
}
}
}
class MyView: UIView {
convenience init(_ backgroundColor:UIColor, _ cornerRadius:CGFloat) {
self.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = backgroundColor
self.layer.cornerRadius = cornerRadius
}
}
Explanation:
This is fairly simple code. The intent was to create as deeply nested a view hierarchy as possible, and, using auto layout, have two main variables: cornerRadius (the view's corner radius) and insetValue (the "frame's" inset). These two variables can be adjusted for experimenting.
The bulk of the logic is in viewWillLayoutSubviews, where the root view frame size is know. Since I'm using 5 different background colors, I'm calculating how many views can fit in the hierarchy. Then I'm creating them, followed by creating the view hierarchy, and finally I'm adding the constraints.
Experimenting and conclusions:
I was able to see what your concern is - yes, if a view's size components are smaller than the corner radius, you end up with inconsistent looking corners. But these values are pretty small - pretty much 10 or less. Most views are unusable at that size. (If I recall even the HIG suggests that a button should be no less than 40 points in size. Sure, even Apple breaks that rule. Still.)
If your 'insetValueis sufficiently larger than the corner radius, you should never have an issue. Likewise, using the iMessage scenario, a singleUILabelcontaining text and/or emoticons should have enough height that a noticeablecornerRadius` can be had.
The key point to set things like cornerRadius and insetValue is in viewWillLayoutSubviews, when you can decide (1) which is the smaller dimension, height or width, (2) how deeply you can nest views, and (3) how large of a corner radius you can set.
Use auto layout! Please note the absolute lack of frames. Other than determining the root view's dimensions at the appropriate time, you can write very compact code without worrying about device size or orientation.
I have created UITextFields dynamically. Now i want to refer to the TextFields to check for some constraints. How do i do so?
func displayTextBox1(height: Int, placeHolder: String, xtb: Int, ytb: Int, lableName: String, xl: Int, yl: Int) {
DispatchQueue.main.async{
self.textField = UITextField(frame: CGRect(x: xtb, y: ytb, width: 343, height: height))
self.textField.textAlignment = NSTextAlignment.left
self.textField.textColor = UIColor.black
self.textField.borderStyle = UITextBorderStyle.line
self.textField.autocapitalizationType = UITextAutocapitalizationType.words // If you need any capitalization
self.textField.placeholder = placeHolder
print("hi")
self.view.addSubview(self.textField)
self.displayLabel(labelName: lableName, x: xl, y: yl)
}
}
You can set constraints programmaticaly using the sample explained code below:
let constraint = NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.width, multiplier: 1, constant: -50)
NSLayoutConstraint.activate([constraint])
As you can see, I am creating a constraint for item textField which width should be equal to width of view multiplied by 1 minus 50. That means the width of your textField will be 50 pixels less than the width of the view. The last line of code activates given set of created constraints.
Welcome to Stackoverflow, and hope you're enjoying learning Swift!
I'm going to make some assumptions based on your code snippet:
the details you need to create the textfield and label (position, placeholder text etc) are coming from some service that operates on a background thread (perhaps a HTTP request?) which is why you're using DispatchQueue.main.async to perform UI events back on the main thread.
there will be multiple textfield/label pairs that you're going to configure and add to the interface (not just a single pair)... perhaps a 'todo' list sort of app where a label and textfield let people enter a note (more on this in part 2 of answer) which is why these views (and constraints) are being created in code rather than in a storyboard.
you want to invest in a constraint-based layout rather than frame-based positioning.
Answer part 1
If any of those assumptions are incorrect, then parts of this answer probably won't be relevant.
But assuming the assumptions are correct I suggest a couple things:
Use a separate helper methods to create a textfield/view and return the result (rather than doing everything in a single method) -- methods that have a single purpose will make more sense and be easier to follow.
Don't use a mixture of setting view position/size with frame and constraints - use one approach or the other (since you're new it will be easier to keep a single mental model of how things are working rather than mixing).
Here's a snippet of what a view controller class might start to look like:
import UIKit
class ViewController: UIViewController {
func triggeredBySomeEvent() {
// assuming that you have some equivilent to `YouBackgroundRequestManager.getTextFieldLabelDetails`
// that grabs the info you need to create the label and textfield on a background thread...
YouBackgroundRequestManager.getTextFieldLabelDetails { tfPlaceholder, tfHeight, tfX, tfY, labelName, labelX, labelY in
// perform UI work on the main thread
DispatchQueue.main.async{
// use our method to dynamically create a textfield
let newTextField: UITextField = self.createTextfield(with: tfPlaceholder, height: tfHeight)
// add textfield to a container view (in this case the view controller's view)
self.view.addSubview(newTextField)
// add constraints that pin the textfield's left and top anchor relative to the left and top anchor of the view
newTextField.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: tfX).isActive = true
newTextField.topAnchor.constraint(equalTo: self.view.topAnchor, constant: tfY).isActive = true
// repeat for label...
let newLabel: UILabel = self.createLabel(with: labelName)
self.view.addSubview(newLabel)
newLabel.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: labelX).isActive = true
newLabel.topAnchor.constraint(equalTo: self.view.topAnchor, constant: labelY).isActive = true
}
}
}
// create, configure, and return a new textfield
func createTextfield(with placeholder: String, height: CGFloat) -> UITextField {
let textField = UITextField(frame: .zero) // set the frame to zero because we're going to manage this with constraints
textField.placeholder = placeholder
textField.textAlignment = .left
textField.textColor = .black
textField.borderStyle = .line
textField.autocapitalizationType = .words
// translatesAutoresizingMaskIntoConstraints = false is important here, if you don't set this as false,
// UIKit will automatically create constraints based on the `frame` of the view.
textField.translatesAutoresizingMaskIntoConstraints = false
textField.heightAnchor.constraint(equalToConstant: height).isActive = true
textField.widthAnchor.constraint(equalToConstant: 343.0).isActive = true
return textField
}
// create, configure and return a new label
func createLabel(with labelName: String) -> UILabel {
let label = UILabel()
label.text = labelName
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
}
Answer part 2
I'm struggling to imagine the situation where you actually want to do this. If you are making a UI where elements repeat themselves (like a todo list, or maybe a spreadsheet-type interface) then this approach is not the right way to do.
If you want to create a UI where elements repeat themselves as repeating elements in a single column you should investigate using a UITableViewController where you create a cell that represents a single element, and have a tableview manage that collection of cells.
If you want to create a UI where elements repeat themselves in any other way than a vertical list, then you investigate using UICollectionViewController which is a little more complex, but a lot more powerful.
Apologies if this answer goes off-topic, but hope that inspires some ideas that are useful for you.
I try to change size and position of searchController.searchBar for exemple
i cahnge y from 0 to 100 but i can see any change in my programme .
here is my code :
placesClient = GMSPlacesClient.sharedClient()
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
searchController?.searchBar.sizeThatFits(CGSize(width: 600, height: 44.0 ))
self.definesPresentationContext = true
searchController?.searchBar.frame = (CGRectMake(0, 100,600, 44.0))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: (searchController?.searchBar)!)
// Keep the navigation bar visible.
searchController?.hidesNavigationBarDuringPresentation = false
searchController?.modalPresentationStyle = UIModalPresentationStyle.Popover
picture
can any body help me please ?
You are able to change the size of a search bar or text field as another example by using the Size Inspector within that item. Also, when you set constraints for the object you can set height or width to anything you wish...within limits of the screen. Therefore, a text field with a set height of 30 can now be set to 50 or whatever you desire to fit your aesthetic. I found this by once searching for myself and finally came across it in a Udemy course for iOS 9 with Mark Price. I would forward the link but it was a paid course. Good luck!