I'm trying to centre a UIView in another view (so like a pop up view), but whatever I do, I just cannot align it centrally!
func loadPopUpView() {
let customView = CGRect(x: view.center.x, y: view.center.y, width: 100, height: 100)
popUpView = UIView(frame: customView)
popUpView.backgroundColor = UIColor.black
view.addSubview(popUpView)
popUpView.isHidden = false
}
I've changed the background colour to black just so I know when it appears.
I cannot do this with storyboard because it's going on a tableView, so I'm trying to do it programmatically. Result Image
You also need to minus half value of your custom view height and width from x and y like below:
let customView = CGRect(x: view.center.x - 50, y: view.center.y - 50, width: 100, height: 100)
You are telling it to put top left corner in the center, hence you need to let it get half size in position.
let customView = CGRect(x: view.center.x-50, y: view.center.y-50, width: 100, height: 100)
Another solution is the use anchorpoints.
customView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
customView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
customView.heightAnchor.constraint(equalToConstant: 100).isActive = true
customView.widthAnchor.constraint(equalToConstant: 100).isActive = true
On CGRect(x:, y:, width:, height:), the point (x,y) is the origin. In iOS, that's the top left point.
On the CGRect doc:
In the default Core Graphics coordinate space, the origin is located
in the lower-left corner of the rectangle and the rectangle extends
towards the upper-right corner. If the context has a
flipped-coordinate space—often the case on iOS—the origin is in the
upper-left corner and the rectangle extends towards the lower-right
corner.
So to fix this:
let width = 100.0
let height = 100.0
let customViewFrame = CGRect(x: view.center.x - width/2.0, y: view.center.y - height/2.0, width: width, height: height)
Another solution would be to apply the center once the frame (especially the width/size) have been set.
let customViewFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
customViewFrame = view.center
Related
I have created this width animation which creates a border-bottom for my TextField and I want to animate it so that when the user clicks on the TextField the CALayer goes from left to right and then appear. the problem is that the animation start from the center and then the width grows to each side while I want it to start from left to right... What am I missing ?
override func didAddSubview(_ subview: UIView) {
let shape = CALayer()
shape.frame = CGRect(x: 0, y: self.height, width: self.width, height: 1)
shape.frame.origin.x = 0
shape.backgroundColor = UIColor.red.cgColor
self.layer.addSublayer(shape)
let boundsAnim:CABasicAnimation = CABasicAnimation(keyPath: "bounds.size.width")
boundsAnim.fromValue = 0
boundsAnim.toValue = self.width
boundsAnim.duration = 2
shape.add(boundsAnim, forKey: "bounds.size.width")
}
Set the anchor point to the left side
shape.anchorPoint = CGPoint(x: 0, y: 0.5)
i'm trying to show a layout at from the bottom of an imageview to a predefined height. Unfortunately its shown at the top, not at the bottom. This is my code i have tried:
var frm: CGRect = firstImageOverlay.frame
frm.origin.x = frm.origin.x
frm.origin.y = frm.origin.y
frm.size.width = frm.size.width
frm.size.height = frm.size.height
firstImageOverlay.frame = frm
UIView.animate(withDuration: 0.5, animations: {
self.progressA.frame = CGRect(x: frm.origin.x, y: frm.origin.y, width: frm.size.width, height: self.firstImageOverlay.frame.height*CGFloat(0.4))
})
and here is the result of this code:
the light gray part shows the layout at the left imageview. this should be shown from the bottom till the defined height
iOS uses a reflected cartesian coordinate system with 0,0 in the top right and positive y pointing down and positive x pointing right, so when you draw at x: frm.origin.x, y: frm.origin.y you are saying draw from the top left down to the width and height.
You need to adjust your y coordinate down:
self.progressA.frame = CGRect(x: frm.origin.x, y: frm.origin.y + self.firstImageOverlay.frame.height*CGFloat(0.6), width: frm.size.width, height: self.firstImageOverlay.frame.height*CGFloat(0.4))
I have a UILabel and I want to change its background view. Somebody advised me to use bezier but I want a simple variant.
My label:
What it should look like:
Since these are not very complex shapes, I would suggest creating two white UIViews, rotating them 45 degrees and then adding them as subviews to the UILabel. The code would look something like this:
myLabel.clipsToBounds = true
// create the triangle on the left side of the label
let leftSquare = UIView(frame: CGRect(x: myLabel.frame.height / -2, y: 0, width: myLabel.frame.height, height: myLabel.frame.height))
leftSquare.translatesAutoresizingMaskIntoConstraints = true
leftSquare.isUserInteractionEnabled = false
leftSquare.backgroundColor = UIColor.white
leftSquare.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 4)) // 45 degree rotation
// create the triangle on the right side of the cell
let rightSquare = UIView(frame: CGRect(x: myLabel.bounds.width - (myLabel.frame.height / 2), y: 0, width: myLabel.frame.height, height: myLabel.frame.height))
rightSquare.translatesAutoresizingMaskIntoConstraints = true
rightSquare.isUserInteractionEnabled = false
rightSquare.backgroundColor = UIColor.white
rightSquare.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 4))
// add squares to label
myLabel.addSubview(leftSquare)
myLabel.addSubview(rightSquare)
myLabel.sendSubview(toBack: leftSquare)
myLabel.sendSubview(toBack: rightSquare)
I have a button which contains text and an image but I would like the image to have a corner radius on it. When I try to apply the corner radius to the button, it applies to the whole button which I guess is correct. How can I set the corner radius to the image instead?
Here's my code:
let titleButton = UIButton()
titleButton.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
titleButton.setTitle("display name", for: .normal)
titleButton.setImage(#imageLiteral(resourceName: "imgName"), for: .normal)
titleButton.layer.masksToBounds = true
titleButton.layer.cornerRadius = titleButton.frame.width / 2
self.navigationItem.titleView = titleButton
An UIButton has an UIImageView on it. So to set corner, simply set radius to the layer of the image view
titleButton.imageView?.layer.cornerRadius = 5
if you want rounded then set height and width equal
titleButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
titleButton.layer.cornerRadius = titleButton.frame.size.width / 2.0
titleButton.layer.masksToBounds = true
And If you want corner rounded only then
titleButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
titleButton.layer.cornerRadius = 10
titleButton.layer.masksToBounds = true
If you want the cornerRadius to be on the button itself and not the image add the following line:
titleButton.clipsToBounds = true
I have the following code that rounds an already existing rectangle from the scene builder...
btOk.path = UIBezierPath(roundedRect: CGRect(x: -62.5,y:-25,width: 125, height: 50), cornerRadius: 10).cgPath
However, if i try to use the same init to round the corners of another rectangle that is much larger, it does not even come close to working. It just makes the width HUUUUGE (imagine Trump).
scene.enumerateChildNodes(withName: "//*"){
node,stop in
if let name = node.name{
if name.contains("round"){
if let shapeNode = node as? SKShapeNode{
print(shapeNode.frame.width) //500
shapeNode.path = UIBezierPath(roundedRect: CGRect(x: -250,y:-100,width: 500, height: 200), cornerRadius: 50).cgPath
print(shapeNode.frame.width) //5735
}
}
}
}
btOk is a SKShapeNode as well. What am i missing between the two that is so different? One thing to note is i am enumerating through the children of the scene like this because this scene is in a SKReferenceNode. Perhaps that has something to do with it?
EDIT
Taking direction from #Ron Myschuk, i've solved this and since it's such a PITA, i also created an extension. So now i can round the corners of any SKShapeNode very easily when needed. Even if it was created in the scene editor. Note, this should only be used if there are no children of the shape node. Otherwise those children will be removed also.
extension SKShapeNode {
func roundCorners(topLeft:Bool,topRight:Bool,bottomLeft:Bool,bottomRight:Bool,radius: CGFloat,parent: SKNode){
let newNode = SKShapeNode(rect: self.frame)
newNode.fillColor = self.fillColor
newNode.lineWidth = self.lineWidth
newNode.position = self.position
newNode.name = self.name
self.removeFromParent()
parent.addChild(newNode)
var corners = UIRectCorner()
if topLeft { corners = corners.union(.bottomLeft) }
if topRight { corners = corners.union(.bottomRight) }
if bottomLeft { corners = corners.union(.topLeft) }
if bottomRight { corners = corners.union(.topRight) }
newNode.path = UIBezierPath(roundedRect: CGRect(x: -(newNode.frame.width / 2),y:-(newNode.frame.height / 2),width: newNode.frame.width, height: newNode.frame.height),byRoundingCorners: corners, cornerRadii: CGSize(width:radius,height:radius)).cgPath
}
}
And use it like so...
aShapeNode.roundCorners(topLeft: true, topRight: true, bottomLeft: false, bottomRight: false, radius: 5,parent: popup)
Not what you're going to want to hear but it's because you cannot set the width of an SKShapeNode in the Scene editor (To my knowledge). In order to get that ShapeNode to have a width of 500 you would have had to adjust the xScale. The xScale then reapplies itself to the path when you adjust it (kind of growing exponentially). If you create the SKShapeNode in code there is no problem adjust the rounded corners
let round = SKShapeNode(rect: CGRect(x: 0, y: -150, width: 500, height: 200))
round.fillColor = .red
addChild(round)
print(round.frame.width)
round.path = UIBezierPath(roundedRect: CGRect(x: -250, y: -100, width: 500, height: 200), cornerRadius: 50).cgPath
print(round.frame.width)
Edit
If you have your heart set on using the Scene editor you can place your ShapeNode and stretch it to where you want it then you could just do a small conversion in code to get the results that you want
if let round = self.childNode(withName: "biground") as? SKShapeNode {
let biground = SKShapeNode(rect: round.frame)
biground.fillColor = round.fillColor
biground.position = round.position
addChild(biground)
round.removeFromParent()
print(biground.frame.width)
biground.path = UIBezierPath(roundedRect: CGRect(x: -250, y: -100, width: 500, height: 200), cornerRadius: 50).cgPath
print(biground.frame.width)
}
this just recreates the shape in code based on what you outlined in the Scene editor and rounds the edges perfectly
edit 2
I've always been under the impression that SKShapeNodes are really inefficient (i'm pretty sure they leaked memory as well). So i always setup my round rectangles as so.
let outsideTexture = SKTexture(imageNamed: "round_square_white")
let outside = SKSpriteNode(texture: outsideTexture)
let insetX: CGFloat = 20
let insetY: CGFloat = 20
let cellSize = CGSize(width: 500.0, height: 500.0)
outside.centerRect = CGRect(x: CGFloat(insetX / outside.size.width), y: CGFloat(insetY / outside.size.height), width: CGFloat((outside.size.width - insetX * 2) / outside.size.width), height: CGFloat((outside.size.height - insetY * 2) / outside.size.height))
//outside.position = CGPointMake(gameModel.gameWidth / 2, (outside.size.height) / 2);
outside.xScale = cellSize.width / outside.size.width
outside.yScale = cellSize.height / outside.size.height
outside.zPosition = 10
outside.position = CGPoint(x: CGFloat(0), y: CGFloat(-0.35 * self.size.height / 2))
self.addChild(outside)
worth noting that this lays out a rounded square/rectangle perfectly however similar to the scale issues from the scene editor you have to place an empty cell over this to add children to, otherwise they scale to the rounded square.