Align and anchor images in iOS layout (or swift) - ios

I have two images and I would like to align horizontally, but the second image, but the second image must be aligned and anchored at the bottom of the first image, I post a picture to understand it better.
Picture here
I think there isn't method to do so via the xcode editor.
Thanks guys!
Edit: (I accidentally deleted my comment and I can not comment)
#Rory McKinnel I did it thanks, now the problem is when the screen has a different size, because the images get bigger but I can not change the offset value setted in the layout editor of xcode.
I have do this:
class TopRow: UITableViewCell {
#IBOutlet weak var imgTop: UIImageView!
#IBOutlet weak var OffsetWhiteCircle: NSLayoutConstraint!
#IBOutlet weak var imgWhiteCircle: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
let heightImgWhite = imgWhiteCircle.frame.size.height
let div = Float(heightImgWhite / -2)
OffsetWhiteCircle.constant = CGFloat(div)
}

Align image 2 with image 1 centres horizontally.
Anchor the top edge of image 2 to be the bottom edge of image 1 with an offset equal to half the image 2 height. This will make image 2 show half way up from the bottom of image 1.
That should give you what you have in the diagram.
This relies on image 2 always having the same height. If the height of image 2 can change, you could link the offset constraint to an IBOutlet and set the offset in your code when you know the image size.

Related

Add label to Image in Swift

Im new to swift and working on app which displays number of unread messages like below image within the app
Counter will increase/decreases as the new messages gets added/read
In order to display that, I have Image with mail icon and wanted to add that green label as badge which shows the number of unread messages
I was thinking to add circular label to image but couldnt figure out how to add that or find any references. Please assist
I was thinking to add circular label to image but couldnt figure out
how to add that or find any references. Please assist.
Welcome to Stackoverflow. There are lots of resources out there on how to round a view, like: https://www.appcoda.com/ios-programming-circular-image-calayer/
You are correct, one way to do what you want is to add a circular label. That's it. Now how to add a circular label? You round the corner of your view by giving its cornerRadius the half of its height.
Position the label to your desired position (with constraints) referencing your message icon.
For example:
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Corner radius to 25 height / 2
self.label.layer.cornerRadius = 12.5
self.label.clipsToBounds = true
// Border
self.label.layer.borderWidth = 2
self.label.layer.borderColor = UIColor.black.cgColor
}
}
Result:

Scale affine transformation scaling differently on iOS7 and iOS8/iOS9 in Swift

I am using scale affine transformations in Swift and have noticed CGAffineTransformMakeScale does not work the same on all iOS versions. To demonstrate the differences, I have created a new Xcode 7 project, set up three test boxes on the Xcode Storyboard running on an iOS7 device, iOS8 simulator and iOS9 simulator.
Box A - has no constraints applied and is positioned centre top on the Storyboard
Box B - has height and width set along with center horizontal and center vertical alignment constraints.
Box C - has height and width set along with bottom space and center horizontal alignment constraints.
Boxes are then scaled to 0.5 using the below code.
Note: the pink areas aren't boxes or containers, but are used to highlight the position of the blue boxes after a scale affine transformation has occurred.
Results:
iOS7 there are problems- while all boxes halve their size, two boxes, A and C, don’t remain centred in place.
iOS8/iOS9 works as expected- all boxes halve their size and remain centered in place whether or not constraints are applied.
Questions:
What is causing this problem and how can it be best corrected and solved so that all iOS7/8/9 versions work identically?
CGAffineTransformMakeScale on iOS7 - does not scale as expected :-(
CGAffineTransformMakeScale on iOS8/iOS9 - scales as expected :-)
Code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ButtonA: UIButton!
#IBOutlet weak var ButtonB: UIButton!
#IBOutlet weak var ButtonC: UIButton!
#IBAction func ButtonScale(sender: AnyObject) {
self.ButtonA.transform = CGAffineTransformMakeScale(0.5, 0.5)
self.ButtonB.transform = CGAffineTransformMakeScale(0.5, 0.5)
self.ButtonC.transform = CGAffineTransformMakeScale(0.5, 0.5)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
This is described by Constraints & transformations -
How Auto Layout quietly became transform-friendly in iOS 8.
Basically, in iOS7 and older you should not set constraints on values that are on different sides of a transformation.

How to easily collapse vertical space around label when its text is nil?

Suppose I have three labels that are laid out below each other in a column. The uppermost label's top edge is pinned to the superview's top edge. All following labels' top edges are pinned to the preceding label's bottom edge. The leading and trailing edges of all labels are pinned to the leading and trailing edge of the superview. Here's what it looks like in Interface Builder (I added a blue background on every label to visualize its extent).
In the simulator the result looks like this.
All labels are connected to outlets in a view controller.
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
When I set the text of label2 to nil
label2.text = nil
the label itself collapses.
However, the top and bottom spaces around the label do not collapse. This is evident by the fact that there is no blue background on the middle label in the last screenshot. As a result, the space between label1 and label3 is double the space of the layout in the first screenshot.
My question is - on iOS8 - what is the easiest way to collapse either the middle label's top or bottom space so that the two remaining labels still use the vertical spacing defined in the original layout? To be clear, this is the result I want to achieve.
Options I've found so far:
Bottom/Top Spacing Constraint Outlet
Define an outlet for the middle label's top or bottom spacing constraint.
#IBOutlet weak var spacingConstraint: NSLayoutConstraint!
Store the constraint's initial constant into a variable (e.g. in awakeFromNib or viewDidLoad).
private var initialSpacing: CGFloat!
override func viewDidLoad() {
initialSpacing = spacingConstraint.constant
...
Set the constraint's constant to zero whenever the text is set to nil or back to its initial value when the text is not nil.
spacingConstraint.constant = label2.text == nil ? 0 : initialSpacing
This approach feels a bit clumsy since it requires two additional variables.
Height Constraint Outlet
Set the vertical spacing around the middle label to zero and increase its height by the same amount. Define an outlet for the height constraint and proceed as above, setting the height to zero when the text is nil and back to it's initial value when the height is not nil.
This is still as clumsy as the previous approach. In addition, you have to hardcode the spacing and cannot use the built-in default spacings (blank fields in Interface builder).
UIStackView
This is not an option since UIStackView is only available on iOS 9 and above.
I'm using this UIView category for this purpose.
It extends UIView by adding two more property named fd_collapsed and fd_collapsibleConstraints using objective-c runtime framework. You simply drag constraints that you want to be disabled when fd_collapsed property set to YES. Behind the scene, it captures the initial value of these constraints, then set to zero whenever fd_collapsed is YES. Reset to initial values when fd_collapsed is NO.
There is also another property called fd_autocollapsed
Not every view needs to add a width or height constraint, views like UILabel, UIImageView have their Intrinsic content size when they have content in it. For these views, we provide a Auto collapse property, when its content is gone, selected constraints will collapse automatically.
This property automatically sets fd_collapsed property to YES whenever specified view has content to display.
It's really simple to use. It's kinda shame that there is no builtin solution like that.
Your solutions are good enough for me and I'd do Bottom/Top Spacing Constraint Outlet solution but since you want something different. You can use this third party: https://github.com/orta/ORStackView It has iOS7+ support and do exactly what you need.
This is low-key a pain all perfectionist devs learn about when trying to stack a bunch of labels. Solutions can get too verbose, annoying to folow, and really annoying to implement (ie. keeping a reference to the top constraint... gets annoying once you do it multiple times, or just change the order of the labels)
Hopefully my code below puts an end to this:
class MyLabel: UILabel {
var topPadding: CGFloat = 0
override func drawText(in rect: CGRect) {
var newRect = rect
newRect.origin.y += topPadding/2
super.drawText(in: newRect)
}
override var intrinsicContentSize: CGSize {
var newIntrisicSize = super.intrinsicContentSize
guard newIntrisicSize != .zero else {
return .zero
}
newIntrisicSize.height += topPadding
return newIntrisicSize
}
}
Usage:
let label = MyLabel()
label.topPadding = 10
// then use autolayout to stack your labels with 0 offset
Granted, its only for top padding, but that should be the only thing you need to layout your labels properly. It works great with or without autolayout. Also its a big plus not needing to do any extra mental gymnastics just to do something so simple. Enjoy!

Get View height based on margin constraints after runtime Swift

I have a circle in the centre of a screen with a margin constraint of 50 on either end. Hence, the width of the circle is dependent on the screen size.
So, what I tried was this:
Approach 1
I set up the margins in the storyboard to define the circle width (50 on left and right)
Then I used the following code:
#IBOutlet weak var helpButHeight: NSLayoutConstraint!
#IBOutlet weak var helpBut: UIButton!
ViewDidLoad:
helpButHeight.constant = helpBut.frame.size.width
This didn't work.
Since the screen width is 400, and the margin is 50 on either end, then helpBut.frame.size.width should have given me 300.
Instead it gave me 46.
Approach 2
This was my work-around:
ViewDidLoad:
let screenSize: CGRect = UIScreen.mainScreen().bounds
helpButHeight.constant = screenSize.width - 100
because 100 = 50 + 50, the two margins.
Works fine !
Question
Why did I have to do this? Why did the first approach not work? Why 46 and not 300?
The reason is that constraints haven't kicked in, in the viewDidLoad function. The lifecycle looks something like
viewDidLoad -- Constraints haven't set
viewWillAppear -- Constraints haven't set
viewWillLayoutSubviews -- Constraints are setting
viewDidLayoutSubviews -- Constraints are set
viewDidAppear -- Constraints are set
If you want any view to be in center just put the horizontal/vertical center constraint...No code required.. If you want to have padding just put the left and right constraints...Just to remind you don't use both of them together...It'll break...

Crop an image in swift

I'm building an image cropping system, and here is a basic simplification of the 3 elements in a view controller i organized to achieve this:
#IBOutlet weak var containerView: UIView! // contains the 2 elements below
#IBOutlet weak var originalImage: UIImageView! //fit in 100% space of above container
#IBOutlet weak var imageGrabber: UIView! //Rectangle on top of originalImage
So we have a container containing image originalImage. Container is resized so image occupies the entire space. Then, we have a rectangle view imageGrabber on top of the image.
The goals here are:
1) Grab rectangle coordinate from the imageGrabber, determined from its position in containerView.
2) Crop originalImage from those rectangle coordinate
So the final result, is a cropped image, corresponding to the area covered by imageGrabber.
Do you know a way to accomplish these ?
If I understand your question correctly all you have to do is
1. Get the final frame of your imageGrabber .
2. Get the same frame from the containerVIew which contains the image, you can user drawRect method to draw the image of size as your imageGrabber.

Resources