I have two methods:
private func showPicker() {
UIView.animate(withDuration: 0.3,
animations: {
var pickerFrame = self.vPickerWrapper.frame
pickerFrame.origin.y = self.view.frame.size.height - 300.0
self.vPickerWrapper.frame = pickerFrame
self.bIsShowingPicker = true
self.view.layoutIfNeeded()
self.view.updateConstraintsIfNeeded()
})
}
private func closeThePicker() {
UIView.animate(withDuration: 0.3,
animations: {
var pickerFrame = self.vPickerWrapper.frame
pickerFrame.origin.y = self.view.frame.size.height + 1
self.vPickerWrapper.frame = pickerFrame
self.bIsShowingPicker = false
self.view.layoutIfNeeded()
self.view.updateConstraintsIfNeeded()
})
....
the first method is called when the user touches a text field that requires them to populate from the pickerview. The second method is called when either the close or select button is touched within the wrapper.
The wrapper is created in storyboard and has constraints (trailing, leading bottom) to position it off screen. I initially made the bottom constraint a IBOutlet and wanted to change its constant to handle the animation. (Which I do with other objects in this VC). I then tried to work with CGAffineTransform.translate and .identity and finally dropped down to working with the frame. In all three cases the wrapper will display but not close (move back off screen).
Here's the value of vPickerWrapper after I set its frame to pickerFrame:
pickerFrame CGRect (origin = (x = 0, y = 668), size = (width = 375, height = 300))
which is what I am expecting, just not any animation in the sim. The view never moves. Any idea why the display is not showing it in the new coordinates?
Related
I am trying to animate a UIView shrinking and moving to a different center point. It starts when the user taps a certain UICollectionViewCell, a new UIView is made, centred on the cell, and starts expanding until it fills the entire screen. This works fine. I store the original center point of the cell in the new UIView (custom class with property originCenter).
let expandingCellView = SlideOverView(frame: cell.bounds)
expandingCellView.center = collectionView.convert(cell.center, to: self)
expandingCellView.textLabel.text = cell.textLabel.text
expandingCellView.originWidth = cell.bounds.width
expandingCellView.originHeight = cell.bounds.height
expandingCellView.originCenter = self.convert(expandingCellView.center, to: self)
expandingCellView.originView = self
self.addSubview(expandingCellView)
expandingCellView.widthConstraint.constant = self.frame.width
expandingCellView.heightConstraint.constant = self.frame.height
UIView.animate(withDuration: 0.3, animations: {
expandingCellView.center = self.center
expandingCellView.layoutIfNeeded()
})
This code works perfectly fine. Now I have added a button to that expanded view, which executes the following code:
widthConstraint.constant = originWidth
heightConstraint.constant = originHeight
print(self.convert(self.originCenter, to: nil))
UIView.animate(withDuration: 0.5, animations: {
self.center = self.convert(self.originCenter, to: nil)
self.layoutIfNeeded()
}, completion: { (done) in
// self.removeFromSuperview()
})
The shrinking of the view to its original size works fine. The only thing that doesn't work is the centering. I take the original cell centerpoint, and convert it to this views coordinate system. This produces coordinates which I think are correct. However all the views just move to the top left of the screen.
Below is a link to a screen recording. The first UIView prints its new centerpoint as (208.75, 411.75) and the second UIView I open prints its center as (567.25, 411.75). These values seem correct to me, however they don't move to this point, as you can see in the video. Any way I can fix this?
Even when setting the new center to for instance CGPoint(x: 500, y: 500), the view still moves to x = 149.5 and y = 149.5
Video: https://streamable.com/pxemc
Create a var that has a weak reference to the cell
weak var selectedCell: UICollectionViewCell!
assign the selected cell in CollectionView delegate didSelectItemAt call
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedCell = collectionView.cellForItemAtIndexPath(indexPath)
}
after add expandingCellView as subview do this to make it go from cell size to full screen
// make expanding view the same size as cell
expandingCellView.frame = selectedCell.frame
// animate
UIView.animate(withDuration: 0.5, animations: {
self.expandingCellView.layoutIfNeeded()
self.expandingCellView.frame = self.view.frame
}, completion: { (_) in
self.expandingCellView.layoutIfNeeded()
})
just reverse it to make it small again like the cell size
// animate
UIView.animate(withDuration: 0.5, animations: {
self.expandingCellView.layoutIfNeeded()
self.expandingCellView.frame = self.selectedCell.frame
}, completion: { (_) in
self.expandingCellView.layoutIfNeeded()
})
frame use global position.
frame vs bounds
I am working on a transition during which a collectionView cell expands to reveal new elements.
In order to prevent the new elements from animating with the cell, I removed them from the animated view.
Here is an image of the view hierarchy
After I remove the elements from the animated view, they are also not being clipped by the cell anymore, revealing them before the cell has expanded.
Right now I can decide between the elements animating with the cell or the elements appearing before they should.
Here is a video of the current state of the animation
You can see the new elements appearing on the cell to the left of the selected one.
Below is the code for my animated transitioning:
let destination = transitionContext.viewController(forKey: .to)
let containerView = transitionContext.containerView
containerView.addSubview(destination.view)
// Initial state
let widthConstraint = destination.header.widthAnchor.constraint(equalToConstant: 500)
let heightConstraint = destination.header.heightAnchor.constraint(equalToConstant: 601)
NSLayoutConstraint.activate([widthConstraint, heightConstraint])
let transform = CATransform3DMakeTranslation(cellFrame.origin.x, cellFrame.origin.y, 0.0)
destination.topView.layer.transform = transform
destination.view.layer.zPosition = 999
containerView.layoutIfNeeded()
let animator = UIViewPropertyAnimator(duration: 4, dampingRatio: 10) {
// Final state
NSLayoutConstraint.deactivate([widthConstraint, heightConstraint])
destination.topView.layer.transform = CATransform3DIdentity
destination.view.layoutIfNeeded()
}
I was thinking about masking the new elements based on the frame of the expanding cell but am not sure how I could make that work.
An alternative solution you could do is to transition the labelingView to show only when it should be present. For example, only show your labelingView once you finish your animation transition.
Some psuedo code..
labelingView.alpha = 0
UIView.animate(withDuration: 1, animations: {
// perform transitions
}) { _ in
labelingView.alpha = 1
}
I solved it an it turned out to be easier than expected.
I simply created a mask based on the initial collectionViewCell and animated it to fullscreen alongside the expanding cell.
let mask = UIView()
mask.frame.origin = CGPoint(x: cellFrame.origin.x, y: cellFrame.origin.y)
mask.frame.size = CGSize(width: 500, height: 601)
mask.backgroundColor = .white
mask.alpha = 1
destination.labelingView.mask = mask
// Final mask state
mask.frame.origin = CGPoint(x: 0.0, y: 0.0)
mask.frame.size = CGSize(width: 1366, height: 1024)
I am simply trying to change the background color of the last element of a CALayer array. Here is my entire View Class, however its only 2-3 lines that I actually try to access the last element of the CALayer.
Here is my progressViewClass and I put comments to where exactly my problem is:
class ProgressBarView: UIView {
//Variables for progress bar
var holdGesture = UILongPressGestureRecognizer()
let animation = CABasicAnimation(keyPath: "bounds.size.width")
var layerHolder = [CALayer]()
var widthIndex = CGPoint(x: 0, y: 0)
var nextXOffset = CGFloat(0.0)
var checkIfFull = CGFloat()
var newLayer : CALayer?
var progressBarAnimationDuration : CFTimeInterval = (MainController.sharedInstance.totalMiliSeconsToRecord / 10)
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
}
func startProgressBar(){
if(RecordingViewController().currentCameraMode == .recordingMode || RecordingViewController().currentCameraMode == .record1stClipMode) {
newLayer = CALayer()
newLayer?.frame = CGRect(x: nextXOffset, y: 0, width: 0, height: self.bounds.height)
newLayer?.backgroundColor = UIColor(red:0.82, green:0.01, blue:0.11, alpha:1.0).cgColor
//print("before \(nextXOffset)")
newLayer?.anchorPoint = widthIndex
animation.fromValue = 0
animation.toValue = self.bounds.width - nextXOffset
animation.duration = progressBarAnimationDuration - ((MainController.sharedInstance.miliSecondsPassed) / 10)
self.layer.addSublayer(newLayer!)
//print("Long Press Began")
newLayer?.add(animation, forKey: "bounds.size.width")
}
else{
stopProgressBar()
}
}
func stopProgressBar(){
if(RecordingViewController().currentCameraMode != .recordingMode){
pauseLayer(layer: newLayer!)
newLayer?.frame = (newLayer?.presentation()!.frame)!
nextXOffset = (newLayer?.frame.maxX)!
layerHolder.append(newLayer!)
print("Layerholder has elements : \(layerHolder.count)")
}
}
// HERE IS MY PROBLEM
func highlightLastLayer(){
print("in highlight last layer Layerholder has elements : \(layerHolder.count)")
// I CAN HIDE THE CALAYER SO I BELIEVE IM ACCESSING THE CORRECT LAYER
// layerHolder.last?.isHidden = true
// This is suppose to change the last element background color to blue but doesnt
layerHolder.last?.backgroundColor = UIColor.blue.cgColor
}
// ALSO MY PROBLEM
func unhighlightLastLayer(){
print("inside unhighlight last layer")
// I CAN HIDE THE CALAYER SO I BELIEVE IM ACCESSING THE CORRECT LAYER
//layerHolder.last?.isHidden = false
// Changes CALayer back to red
layerHolder.last?.backgroundColor = UIColor(red:0.82, green:0.01, blue:0.11, alpha:1.0).cgColor
}
//Function to pause the Progress Bar
func pauseLayer(layer : CALayer){
let pausedTime : CFTimeInterval = layer.convertTime(CACurrentMediaTime(), from: nil)
layer.speed = 0.0
layer.timeOffset = pausedTime
}
}
Simply put, I create a progressView object in my viewController and then call those functions based on certain button input. This view is essentially a progress bar that you'd see in many video recording applications to show how much you have recorded. In the highlightLastLayer, I am trying to grab the last element of the "layerHolder" array and change its color to blue. Simple right? Doesn't work. Any ideas?
Where are you calling highlight and unhighlight. I am pretty sure you are doing this when you are "stopped" or "paused" because thats the only time you add anything to the layerHolder Array. You can only do this when you are not animating because when you are animating the presentation layer is shown instead of the "real" layer. Instead of setting the speed to zero, setup your layer to look like the current animation state and call layer. removeAllAnimations to kill the presentation layer and show the actual layer for your view instead. Now you can make all the changes that you want and they will actually show up.
I am trying to add the Instagram like animation on my app that minimizes the title "Instagram" and moves the display to the entire page.
In my app, i have two UILabels - chapterNameNumberLabel and chapterNameLabels and a UITextView. On scrolling in the UITextView, i want the chapterNameNumberLabel and the chapterNameLabels to minimize and the entire page to be filled with the UITextView contents.
I have tried the below:(used pointers mentioned in the answer here Finding the direction of scrolling in a UIScrollView?
Obtained the below in viewDidLoad():
xLabel = self.chapNameNumberLabel.center.x;
yLabel = self.chapNameNumberLabel.center.y;
widthLabel = self.chapNameNumberLabel.frame.width;
heightLabel = self.chapNameNumberLabel.frame.height;
xNameLabel = self.chapterNameLabel.center.x;
yNameLabel = self.chapterNameLabel.center.y;
widthNameLabel = self.chapterNameLabel.frame.width;
heightNameLabel = self.chapterNameLabel.frame.height;
And called the below method:
func scrollViewDidScroll(scrollView: UIScrollView) {
if(self.lastContentOffset > 0 ){
if (self.lastContentOffset > scrollView.contentOffset.y) {
//print("Up");
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.chapNameNumberLabel.frame = CGRectMake(self.xLabel - 100, self.yLabel - 10, self.widthLabel, self.heightLabel);
self.chapterNameLabel.frame = CGRectMake(self.xNameLabel - 140, self.yNameLabel - 10 , self.widthNameLabel, self.heightNameLabel);
}, completion: nil)
}
else if (self.lastContentOffset < scrollView.contentOffset.y){
print("Down");
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.chapNameNumberLabel.frame = CGRectMake( 100 , -10, 5, 5);
self.chapterNameLabel.frame = CGRectMake(50, -10, 5, 5);
}, completion: nil);
}
}
// update the new position acquired
self.lastContentOffset = scrollView.contentOffset.y
}
The animation is okay, but the problem that i am facing are:
I am unable to get the 'font size minimizing' effect on the labels. The labels in my app just disappear without reducing the font size. What animation type should be used for achieving this font size minimization on scrolling?
Also, the values that i have set on scrolling down(within the scrollViewDidScroll) are
self.chapNameNumberLabel.frame = CGRectMake( 100 , -10, 5, 5);
self.chapterNameLabel.frame = CGRectMake(50, -10, 5, 5); and on scrolling up are trial and error values. The labels are not properly aligned when i rotate the screen left and right(on simulator). Could some one please tell me how to calculate this so that it works on all screen orientations?
I have used Swift 2 , Auto layout.
How do I enable zooming in a UIScrollView?
Answer is here:
A scroll view also handles zooming and panning of content. As the user makes a pinch-in or pinch-out gesture, the scroll view adjusts the offset and the scale of the content. When the gesture ends, the object managing the content view should update subviews of the content as necessary. (Note that the gesture can end and a finger could still be down.) While the gesture is in progress, the scroll view does not send any tracking calls to the subview.
The UIScrollView class can have a delegate that must adopt the UIScrollViewDelegate protocol. For zooming and panning to work, the delegate must implement both viewForZoomingInScrollView: and scrollViewDidEndZooming:withView:atScale:; in addition, the maximum (maximumZoomScale) and minimum (minimumZoomScale) zoom scale must be different.
So:
You need a delegate that implements UIScrollViewDelegate and is set to delegate on your UIScrollView instance
On your delegate you have to implement one method: viewForZoomingInScrollView: (which must return the content view you're interested in zooming). You can also implement scrollViewDidEndZooming:withView:atScale: optionally.
On your UIScrollView instance, you have to set the minimumZoomScale and the maximumZoomScale to be different (they are 1.0 by default).
Note: The interesting thing about this is what if you want to break zooming. Is it enough to return nil in the viewForZooming... method? It does break zooming, but some of the gestures will be messed up (for two fingers). Therefore, to break zooming you should set the min and max zoom scale to 1.0.
Have a read through this Ray Wenderlich tutorial:
http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift
If you follow through the section 'Scrolling and Zooming a Larger Image' it will get a image up and enable you to pinch and zoom.
In case the link gets altered, here's the main info:
Put this code in your view controller (this sets the main functionality):
override func viewDidLoad() {
super.viewDidLoad()
// 1
let image = UIImage(named: "photo1.png")!
imageView = UIImageView(image: image)
imageView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size:image.size)
scrollView.addSubview(imageView)
// 2
scrollView.contentSize = image.size
// 3
var doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "scrollViewDoubleTapped:")
doubleTapRecognizer.numberOfTapsRequired = 2
doubleTapRecognizer.numberOfTouchesRequired = 1
scrollView.addGestureRecognizer(doubleTapRecognizer)
// 4
let scrollViewFrame = scrollView.frame
let scaleWidth = scrollViewFrame.size.width / scrollView.contentSize.width
let scaleHeight = scrollViewFrame.size.height / scrollView.contentSize.height
let minScale = min(scaleWidth, scaleHeight);
scrollView.minimumZoomScale = minScale;
// 5
scrollView.maximumZoomScale = 1.0
scrollView.zoomScale = minScale;
// 6
centerScrollViewContents()
}
Add this to the class:
func centerScrollViewContents() {
let boundsSize = scrollView.bounds.size
var contentsFrame = imageView.frame
if contentsFrame.size.width < boundsSize.width {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0
} else {
contentsFrame.origin.x = 0.0
}
if contentsFrame.size.height < boundsSize.height {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0
} else {
contentsFrame.origin.y = 0.0
}
imageView.frame = contentsFrame
}
And then this if you want the double tap gesture to be recognised:
func scrollViewDoubleTapped(recognizer: UITapGestureRecognizer) {
// 1
let pointInView = recognizer.locationInView(imageView)
// 2
var newZoomScale = scrollView.zoomScale * 1.5
newZoomScale = min(newZoomScale, scrollView.maximumZoomScale)
// 3
let scrollViewSize = scrollView.bounds.size
let w = scrollViewSize.width / newZoomScale
let h = scrollViewSize.height / newZoomScale
let x = pointInView.x - (w / 2.0)
let y = pointInView.y - (h / 2.0)
let rectToZoomTo = CGRectMake(x, y, w, h);
// 4
scrollView.zoomToRect(rectToZoomTo, animated: true)
}
If you want more detail read the tutorial, but that pretty much covers it.
Make sure you set your viewController as the scrollViews delegate and implement:
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
I don't think this is working for iOS 5.0 and Xcode 4.3+
Im looking for the same here, I found this its for images but it may help you.
http://www.youtube.com/watch?v=Ptm4St6ySEI