I'm trying to access the midX and midY properties of the bar button's frame to animate a view appearance from that point. To access them I'm using the following code:
let barButtonItem = self.navigationItem.rightBarButtonItem!
let barButtonItemView = barButtonItem.value(forKey: "view") as? UIView
let barButtonFrame = barButtonItemView?.frame
print ("FRAME", barButtonFrame)
As you can see, there is a print statement at the end of the chunk, and it prints:
FRAME Optional((0.0, 0.0, 46.3333333333333, 44.0))
For some reason, the frame coordinates are both at 0, so the view animation starts from the left top of the screen.
I've looked in many answers from here and almost all of them suggest the solution used above.
If you know why this happens, I would appreciate your help?
Related
I have a view that has a pan gesture recognizer on it to grow the overall height of the view as you swipe it up or down. This works well enough for now but I also wanted to add a drop shadow that I had designed and make sure that it kept with the height of the changing draggable view. However, this is producing some strange effects and I want to see if there is a way to fix this
The relevant code to handle changing the view height and the shadow height is as follows
// SET SHADOW FOR DRAGABLE VIEW PATH IN VIEW DID LOAD
draggableView.layer.shadowColor = UIColor.black.cgColor
draggableView.layer.shadowOffset = CGSize(width: 0, height: 12)
draggableView.layer.shadowOpacity = 0.33
draggableView.layer.shadowRadius = 8
draggableView.layer.shadowPath = UIBezierPath(roundedRect:
draggableView.bounds, cornerRadius: 12).cgPath
// INSIDE THE RECOGNIZER HANDLER
if recognizer.state == .began || recognizer.state == .changed {
translation = recognizer.translation(in: self.view)
recognizer.setTranslation(CGPoint(x: 0.0, y: 0.0), in: self.view)
let endPosition = recognizer.location(in: draggableView) // the posiion at which PanGesture Ended
difference = endPosition.y - startPosition.y
var newFrame = draggableView.frame
newFrame.origin.x = draggableView.frame.origin.x
newFrame.origin.y = draggableView.frame.origin.y + difference
// HERE WE SET THE HEIGHT OF THE VIEW THAT IS BEING "DRAGGED"
newFrame.size.height = draggableView.frame.size.height - difference
// HERE WE UPDATE THE SHADOW PATH WITH THE NEW DRAGGABLE VIEW BOUNDS
draggableView.layer.shadowPath = UIBezierPath(roundedRect: draggableView.bounds, cornerRadius: 12).cgPath
draggableView.frame = newFrame
}
The effect I'm getting is the shadow not quite updating in time with the current pan gesture, here is a gif of what that looks like
I'm hoping there is a way I can get this to work without this weird behavior, I'm fairly new to swift though I understand the concepts it's more about not really knowing all the best ways to go about doing things
You need to invert the order of the following two lines of code
draggableView.layer.shadowPath = UIBezierPath(roundedRect: draggableView.bounds, cornerRadius: 12).cgPath
draggableView.frame = newFrame
The reason is that you are updating the potision (Frame) of your draggableView in the PanGesture delegate method. However, you are updating the shadow frame (line 1) BEFORE updating the position (frame) of the draggable view (line 2). So, inverting the two lines will work for you
Suggestions:
In your case, you are updating frames of two views (draggable and shadow). We can see that the CPU is struggling trying to updating both of them since it's lagging (especially in the shadow part since you are re-drawing it.)
My suggestions is that you can create a parent transparent view and put your current draggable view inside and draw the shadow. This will happen only once in your viewDidLoad. Then inside gesture deletege, you will only update frame of the parent view. In this case, you do not need to update the shadow every time and the dragging will be more smooth
I have a UIScrollView displaying a image. I want to programmatically zoom in to the a rect somewhere near the center (doesn't have to be exact) of the currently visible area. How would I get the coordinates of this rect to use with zoomToRect? Note that this image could be already zoomed in and only showing a fraction of the scrollView content area.
The the X and Y position of that image are relative to the scrollview's contentSize. The area shown on screen is defined by the scrollview's contentOffset.
You then take the position of your scrollview on screen and the position of the selection rectangle on your screen.
Finally you need to do rather simple maths (a few additions/subtractions) for both X and Y using the above values.
Grab UIImageView's frame and call insetBy(dx:dy:):
Returns a rectangle that is smaller or larger than the source
rectangle, with the same center point.
From Apple Documentation
Here's a quick visualisation in a PlayGround:
let blueSquare = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let yellowSquare = UIView(frame: blueSquare.frame.insetBy(dx: 100, dy: 100))
blueSquare.backgroundColor = .blue
yellowSquare.backgroundColor = .yellow
blueSquare.addSubview(yellowSquare)
Will result in this:
I have added a CALayer to an image doing the following :
var menulayer = CALayer()
menulayer.frame = CGRectMake(0.0, 0.0, menuimage.frame.size.width, menuimage.frame.size.height);
menulayer.backgroundColor = bartint.CGColor
menulayer.opacity = 1
menuimage.layer.addSublayer(menulayer)
I want to animate the layer so that it reveals the image from left to right. I have tried this:
let width = CGFloat(0)
CATransaction.begin()
CATransaction.setAnimationDuration(2.0)
self.menulayer.bounds = CGRectMake(0, 0, width , 50)
CATransaction.commit()
But the animation starts at the center of the image, how can I make it start from the left edge of the image ?
So animate the origin of the layer to shift it to the right until your image view is fully revealed. You will need to set menuImage.layer.masksToBounds = true so the cover layer isn't visible as it scrolls out of the frame of the image view.
I built a demo app to check the relationship between layer's anchorpoint, position and frame.
The initial look of the view looks like following:
In the code, I change that red view's anchor point, it will looks like this, which I could understand since change of anchor point will affect that view's frame.
to maintain the view's frame as original one, I used the following code:
We could see from the console's printout the frame has already remained the same.
However the view's final look looks like following, which still changes its position, how could this happen?
All the code looks like this:
Code are as following:
// Reserve original frame
let oldFrame = self.controlledView.frame
// Here I changed the anchorPoint which will cause that view's frame change
self.controlledView.layer.anchorPoint = CGPoint(x: 0, y: 0)
// to avoid the change of frame, set it back
self.controlledView.frame = oldFrame
// From the console's log the frame doesn't change, the red view's final
// location should be the same with the first image. However it is aligned to the right,
// which I could not understand.
From the CALayer Class Reference on Apple Documentation
You specify the value for this property using the unit coordinate space. The default value of this property is (0.5, 0.5), which represents the center of the layer’s bounds rectangle. All geometric manipulations to the view occur about the specified point. For example, applying a rotation transform to a layer with the default anchor point causes the layer to rotate around its center. Changing the anchor point to a different location would cause the layer to rotate around that new point.
From the UIView Class Reference on Apple Documentation
This rectangle defines the size and position of the view in its superview’s coordinate system. You use this rectangle during layout operations to size and position the view. Setting this property changes the point specified by the center property and the size in the bounds rectangle accordingly. The coordinates of the frame rectangle are always specified in points
So from my point of view, once the view is inside another view, when you change the frame you are changing it's center and size relative to his superview and not with itself.
To test my theory, i perform a small example
import UIKit
class ViewController: UIViewController {
var insideView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Setup Inside view
self.insideView = UIView()
var frame: CGRect = CGRectZero
frame.size.height = 40.0
frame.size.width = 40.0
frame.origin.x = self.view.frame.size.width / 2 - 20.0
frame.origin.y = self.view.frame.size.height / 2 - 20.0
self.insideView.frame = frame
self.insideView.backgroundColor = UIColor.redColor()
self.view.addSubview(self.insideView)
NSLog("One layer -> InsideView size: %#", NSStringFromCGRect(self.insideView.frame))
// Output: 2015-05-22 20:13:11.342 test[42680:12030822] One layer -> InsideView size: {{140, 264}, {40, 40}}
// Setup Another layer
var insideLayer: CALayer = CALayer()
insideLayer.backgroundColor = UIColor.blueColor().CGColor
var insideLayerFrame: CGRect = self.insideView.layer.frame;
insideLayerFrame.origin.x = 0.0
insideLayerFrame.origin.y = 0.0
insideLayer.frame = insideLayerFrame
insideLayer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
self.insideView.layer.addSublayer(insideLayer)
NSLog("Two layers -> InsideView size: %#", NSStringFromCGRect(self.insideView.frame))
// Output: 2015-05-22 20:13:11.342 test[42680:12030822] Two layers -> InsideView size: {{140, 264}, {40, 40}}
}
}
So i leave the layer of the view in it's position and add a new that i can manipulate.
And the result is:
Hope this can help :)
I'm trying to scale a view Horizontally but i'm not getting the results i expected.
view.bounds = CGRectMake(view.bounds.origin.x, view.bounds.origin.y , view.bounds.size.width * scaleX, view.bounds.size.height );
view.center = CGPointMake(view.bounds.origin.x, view.center.y);
As you can see at the code above, i want to scale the view just to the right side. This is why i changed the center of the view.
The problem is that the view stills scaling to both sides!
I did the same logic to scale another UIView vertically and got the result i expected.
Just found the problem.
when setting certer, the new point must be relative to frame, not bounds.
so, i get the code:
view.center = CGPointMake(-view.frame.origin.x, view.center.y);
Hope it helps somebody..
Thx for help
If the origin of your view's frame is in the top-left (which is the default), increasing the width of your view should scale it to the right only.
This should work:
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width * scaleX, view.frame.size.height);
You do not need to adjust the origin/center point to scale only to the right.
Try using an affine transform instead:
view.transform = CGAffineTransformMakeScale(scaleX, 1)
And then setting the center.