I am new to iOS UI development. and i am stuck in the below issue.
I am trying to give half of the width of parent view to 2 child views. (See image). I can't set multiplier to less than zero. Both red and green child view should be equal in width, and width should be half of the parent view.
I am using visual studio for development.
You should be able to set the Multiplier to 1:2 as it works with ratios.
I feel this is a bug in the iOS designer in Visual Studio as the Multiplier is a floating point number. from the docs here:
Multiplier: The value of attribute 2 is multiplied by this floating point number. In this case, the multiplier is 1.0.
In Xamarin Studio, iOS Designer a ratio is accepted, Also 1/2 0.5:
And In Xcode:
It even gets added to the source of the xib/storyboard like so:
<constraint id="9" firstItem="3" firstAttribute="width" secondItem="8bC-Xf-vdC" secondAttribute="width" multiplier="1:2"/>
so as a work around you could edit the .xib or .storyboard file, but thats not ideal.
An alternative to setting the multiplier you could set:
RedView
LeadingSpace to superview.leadingspace
TrailingSpace to superview.CenterX
GreenView
LeadingSpace to superview.CenterX
TrailingSpace to superview.trailingspace
This will do the same as setting the width to half the the width size of the superview.
TBH Visual studio's and Xamarin studio's iOS Designers are not very good in comparision to Xcode for adding complicated constraints (this is not that complicated but it still fails). I would suggest trying to edit it in Xcode, if you can.
Update
If you want to add them programatically, add this in ViewDidLoad:
public override void ViewDidLoad()
{
base.ViewDidLoad();
greenView.TranslatesAutoresizingMaskIntoConstraints = false;
redView.TranslatesAutoresizingMaskIntoConstraints = false;
View.AddConstraints(new NSLayoutConstraint[]{
NSLayoutConstraint.Create(redView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.Leading, 1, 0),
NSLayoutConstraint.Create(redView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.CenterX, 1, 0),
NSLayoutConstraint.Create(redView, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, View, NSLayoutAttribute.CenterX, 1, 0),
NSLayoutConstraint.Create(greenView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.CenterX, 1, 0),
NSLayoutConstraint.Create(greenView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.Trailing, 1, 0),
NSLayoutConstraint.Create(greenView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, redView, NSLayoutAttribute.Top, 1, 0),
});
}
It will make a view like so:
Related
If there are multiple UILabels in one UIStoryboard, and they have the same centerY, which means they are at the same line. How can I use autolayout to let them fit in different screen? I hope these UILabels have same font size.
You need to add constraints. There are a couple of ways to add constraints.
If you go to the storyboard you will see a rectangle in between 2 vertical lines. If you click the the view you want to add constraints too (Like the UIView you added) and click that rectangle box thing It gives you options to add constants to the left, right, top, bottom of the view. If you click on the dropdown menu it gives you a list of views around the view your adding constraints too so that you can add constraints relative to that view.
Another way to add constraints is clicking the triangle button in between 2 vertical lines and selecting the option "add missing constraints". Auto layout will then try and determine the best constraints to add automatically.
To delete or modify constraints you can click the "show size inspector" (looks like a ruler) and in the constraints section you should see some constraints if you have added any. You can click and delete them or edit them.
Another option is adding constraints programmatically (below is an example of adding constraints to an image view in a stack view in swift):
let trailingConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self.stackView, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: -5)
let leadingConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.stackView, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 5)
self.stackView.addArrangedSubview(imageView)
self.stackView.addConstraint(trailingConstraint)
self.stackView.addConstraint(leadingConstraint)
In my xamarin iOS app I have a list view, I want to show the loading icon when it is being loading. But, I do not want to disable the whole page. I want the user to still go back if they wish to, using back navigation.
I used this as a reference.
So, I'm trying to set the CGRect Frame, to leave the navigation on the top active and disable the rest of the page during loading status.
I am using something like this: new CGRect(30, 0,0, 0) to leave 30 units from the top but it doesnt work. Can anyone help me out to have a frame to leave just navigation bar and cover rest of the page?
AutoLayout is a much better way to accomplish this task than using frames. Unless you need to target REALLY, REALLY old versions of iOS, AutoLayout is generally the way to go.
I'm assuming you are using a UIViewController and not a UITableViewController. This is an important distinction, because the UITableViewController only allows you to add to the UITableView which makes this task much more challenging.
Add this AddAnOverlay method to your UIViewController class, then call it whenever you want to display an overlay. You will probably need to put overlay in an instance variable so that you can remove it later. Remove it by calling overlay.RemoveFromSuperview() and you're done.
void AddAnOverlay()
{
var overlay = new UIView();
overlay.BackgroundColor = UIColor.Black.ColorWithAlpha(0.45f); // or whatever colo
overlay.TranslatesAutoresizingMaskIntoConstraints = false;
var label = new UILabel();
label.TranslatesAutoresizingMaskIntoConstraints = false;
label.Text = "Loading Fantastic Things!";
var spinner = new UIActivityIndicatorView();
spinner.TranslatesAutoresizingMaskIntoConstraints = false;
spinner.StartAnimating();
overlay.AddSubview(spinner);
overlay.AddConstraint(NSLayoutConstraint.Create(overlay, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, spinner, NSLayoutAttribute.CenterX, 1, 0));
overlay.AddConstraint(NSLayoutConstraint.Create(overlay, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, spinner, NSLayoutAttribute.CenterY, 1, 0));
overlay.AddSubview(label);
// can adjust space between by changing -30 to whatever
overlay.AddConstraint(NSLayoutConstraint.Create(spinner, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, label, NSLayoutAttribute.Top, 1, -30));
overlay.AddConstraint(NSLayoutConstraint.Create(overlay, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, label, NSLayoutAttribute.CenterX, 1, 0));
View.AddSubview(overlay);
View.AddConstraint(NSLayoutConstraint.Create(TopLayoutGuide, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, overlay, NSLayoutAttribute.Top, 1, 0));
View.AddConstraint(NSLayoutConstraint.Create(View, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, overlay, NSLayoutAttribute.CenterX, 1, 0));
View.AddConstraint(NSLayoutConstraint.Create(View, NSLayoutAttribute.Width, NSLayoutRelation.Equal, overlay, NSLayoutAttribute.Width, 1, 0));
View.AddConstraint(NSLayoutConstraint.Create(BottomLayoutGuide, NSLayoutAttribute.Top, NSLayoutRelation.Equal, overlay, NSLayoutAttribute.Bottom, 1, 0));
}
Notice the TopLayoutGuide and BottomLayoutGuide in the NSLayoutConstraint. These represent the top and bottom of your current view controller, so they can be used to size things so they don't hide navigation bars, tab bars, etc.
You have to add the overlay to the table view, which leads you to a centering problem. For this there are some options: recalculate the position of the overlay when scrolling, add an additional layer behind the table view which you reference with autolayout for positioning, ...
I'm using autolayout and you should avoid giving actual numbers, but it is also possible using bounds. Also be aware of if the user navigates away you have to cancel the task!
I am trying to implement something like the image shown (http://i.stack.imgur.com/jKmGO.png) below using auto layout in Swift, but every time I end up with some issues. Can anyone please tell me the best way to implement auto layout for this kind of arrangement in horizontal grid for 3,4,5 or more image views.
Try the following steps
1) first select all of them and add constraint EQUAL WIDTH to images
2) set the leading space between left margin and first image view( let it be d)
3) Similarly set trailing space between the right margin and last imageview (let it be d)
4) Now add constraint HORIZONTAL DISTANCE(d) between all the image view e.g.
LEFT MARGIN-d-IMAGE1-d-IMAGE2-d-IMAGE3-d-IMAGE4-d-RIGHT MARGIN
Checkout Stanford Cs193p lecture no 8. Something similar is considered which was a basic calculator with equal width and height of keys.
Try code below (for horizontal arrangement)
var arrayOfImages = [image1,image2,image3,image4]
var previousImage:UIView? = nil
let horizontalSpaceBetweenImages = 20.0
let viewToUse = self.view //change this view to the one in which you are adding images as subviews
for image in arrayOfImages {
viewToUse.addSubview(image)
if previousItem == nil {
// this is the first item
viewToUse.addConstraint(NSLayoutConstraint(item: viewToUse, attribute: .Leading, relatedBy: .Equal, toItem: image, attribute: .Leading, multiplier: 1.0, constant: 0.0))
}
else {
viewToUse.addConstraint(NSLayoutConstraint(item: image, attribute: .Leading, relatedBy: .Equal, toItem: previousImage, attribute: .Trailing, multiplier: 1.0, constant: horizontalSpaceBetweenImages))
viewToUse.addConstraint(NSLayoutConstraint(item: image, attribute: .Width, relatedBy: .Equal, toItem: previousImage, attribute: .Width, multiplier: 1.0, constant: 0.0))
}
previousImage = image
}
// now add trailing constraint for last image
viewToUse.addConstraint(NSLayoutConstraint(item: arrayOfImages.last, attribute: .Trailing, relatedBy: .Equal, toItem: viewToUse, attribute: .Trailing, multiplier: 1.0, constant: 0.0))
this code sorts out horizontal alignment. It should be trivial to then add vertical alignment constraints (they should be easier to implement)
A collectionView would be the best option for you if you are going to be switching between 3, 4, and 5 images in your view. If you are, however, trying to have a set number of images displaying (number of UIImages stays the same) You can try doing this method:
Everything here is going to be based on the horizontal distance available to you based on the device size. So to start, you need to figure out what height you'll want for the images. Do the heights need to be relative to the width? Or can the heights for each image stay the same?
If the heights need to stay relative to the widths, put an "Aspect Ratio" constraint on each image. If the heights can stay static, place a "height constraint" on each image.
Next you need to set constraints for the distances between the images.
At this point you are still going to have errors because the width for each image will be ambiguous. To fix this, select all the images simultaneously and then add the constraint "Equal Widths." As long as you've made sure that the images are pinned to the top, you should no longer have auto-layout issues at this point.
Let me know if I can be more clear on any of these steps.
I am trying to use auto layout to to position two views like below picture. I know that with iOS 8 and size classes I can create layout for different layouts and it would work. However I am targeting iOS 7 and according to several posts such as https://stackoverflow.com/a/26841899/4170419, iPhone landscape mode of size classes will not work for earlier version. So, how can I position those two views on different orientation according to my picture? Thanks.
Compact width compact height size class will not work on iOS7. It will clash with compact width any height sadly.
Very good summary here: How can Xcode 6 adaptive UIs be backwards-compatible with iOS 7 and iOS 6?
For you particular case, I think you might have to resort to some manual work for iOS7. A possible approach would be:
1) Add a UIView between the label area and the right hand side pinned to the top, bottom, label area leading and trailing to superview with size 0. With no content this should end up 0 width.
2) Add a UIView between the label area and the bottom, pinned top to the label area, leading and trailing to the superview and to bottom with size 0. With no content this should end up 0 height.
3) Add IBOutlets for both views into your controller .h file.
4) In viewDidLoad, create and place the MKMapView into the appropriate view for the current orientation.
This gives you on startup the map view in the location you want.
5) Add orientation change detection to your controller. When the orientation changes, remove the MKMapView from its current view and add it to the view for the new orientation.
Not sure this will work, but it might give you what you want on iOS8 and iOS7 with one piece of code.
You may need to add constraints to the views for width/height or set the MKMapView frame to get the dimensions right for each orientation when layout occurs.
Hopefully somebody may come up with an easier solution, but seems size classes are unlikely to help in this case.
Finally I solved it :) First of all, both of my orientation show that mapview must be aligned to bottom and right of main view. These constraints are fixed. Then just to shut Xcode I set the constraints for portrait orientation.
Since iPad has enough screen state, I applied rotation fix to only iPhone. I am sure it can be written better way so if you have any comments please share. Thanks.
lazy var landscapeLeft: NSLayoutConstraint = {
return NSLayoutConstraint(item: self.myMap, attribute: .Leading, relatedBy: .Equal, toItem: self.idLabel, attribute: .CenterX, multiplier: 1.0, constant: 0)
}()
lazy var landscapeTop: NSLayoutConstraint = {
return NSLayoutConstraint(item: self.myMap, attribute: .Top, relatedBy: .Equal, toItem: self.idLabel, attribute: .Bottom, multiplier: 1.0, constant: 8)
}()
lazy var portraitLeft: NSLayoutConstraint = {
return NSLayoutConstraint(item: self.myMap, attribute: .Leading, relatedBy: .Equal, toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 8)
}()
lazy var portraitBottom: NSLayoutConstraint = {
return NSLayoutConstraint(item: self.myMap, attribute: .Top, relatedBy: .Equal, toItem: self.expectedDateLabel, attribute: .Bottom, multiplier: 1.0, constant: 8)
}()
func fixLayout() {
if (UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Phone) && (UIDevice.currentDevice().orientation.isLandscape) {
self.view.removeConstraint(portraitLeft)
self.view.removeConstraint(portraitBottom)
self.view.addConstraint(landscapeLeft)
self.view.addConstraint(landscapeTop)
} else {
self.view.removeConstraint(landscapeLeft)
self.view.removeConstraint(landscapeTop)
self.view.addConstraint(portraitLeft)
self.view.addConstraint(portraitBottom)
}
}
override func updateViewConstraints() {
super.updateViewConstraints()
fixLayout()
}
UPDATE: I was so naive to think that the code I wrote before was compatible with iOS 7 just because it compiled to ios 7 target and worked on iOS 8 simulator. Thanks to #Rory McKinnel, I found out that I was using methods which are not available on iOS 7. This time, I tested on both iOS 7 and iOS 8 simulators and they worked. I hope that is the end of this problem.
I have 4 buttons. I want them to be 13% of the screen width from the left edge and bottom edge. I'm using Auto Layout and Size Classes. I know I can specify a number of points in Interface Builder with Storyboards but obviously this won't get the job done when going from device to device. Do I need to hook up a constraint through an IBOutlet and calculate the constraint there in code to achieve the desired result? Or is this possible using Interface Builder?
This is special case, which can't be handled in IB (AFAIK). These combo boxes don't contain all available attributes.
Do it in code:
let con = NSLayoutConstraint(item: myView, attribute: .Left, relatedBy: .Equal,
toItem: fullWidthView, attribute: .Width, multiplier: 0.13, constant: 0.0)
Replace myView with your button. Replace fullWidthView with any view which occupies whole width of the device. Typically UIViewController.view.
And do the same thing with .Bottom, .Height and fullHeightView.