TabBarController Transitions getting blank screen issue in iOS - ios

In my application implemented TabBarController Transitions using reference code by Apple Objective-C link And Swift link. But when switch fast between two tabs some times I am getting blank screen, I tried many answers in Stack Overflow but no luck.
Please check below code for reference while doing TabBarController Transitions using Swift
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let containerView = transitionContext.containerView
let fromView: UIView
let toView: UIView
// In iOS 8, the viewForKey: method was introduced to get views that the
// animator manipulates. This method should be preferred over accessing
// the view of the fromViewController/toViewController directly.
if #available(iOS 8.0, *) {
fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
} else {
fromView = fromViewController.view
toView = toViewController.view
}
let fromFrame = transitionContext.initialFrame(for: fromViewController)
let toFrame = transitionContext.finalFrame(for: toViewController)
// Based on the configured targetEdge, derive a normalized vector that will
// be used to offset the frame of the view controllers.
var offset: CGVector
if self.targetEdge == UIRectEdge.left {
offset = CGVector(dx: -1.0, dy: 0.0)
} else if self.targetEdge == .right {
offset = CGVector(dx: 1.0, dy: 0.0)
} else {
fatalError("targetEdge must be one of UIRectEdgeLeft, or UIRectEdgeRight.")
}
// The toView starts off-screen and slides in as the fromView slides out.
fromView.frame = fromFrame
toView.frame = toFrame.offsetBy(dx: toFrame.size.width * offset.dx * -1,
dy: toFrame.size.height * offset.dy * -1)
// We are responsible for adding the incoming view to the containerView.
containerView.addSubview(toView)
let transitionDuration = self.transitionDuration(using: transitionContext)
UIView.animate(withDuration: transitionDuration, animations: {
fromView.frame = fromFrame.offsetBy(dx: fromFrame.size.width * offset.dx,
dy: fromFrame.size.height * offset.dy)
toView.frame = toFrame
}, completion: {finshed in
let wasCancelled = transitionContext.transitionWasCancelled
// When we complete, tell the transition context
// passing along the BOOL that indicates whether the transition
// finished or not.
transitionContext.containerView.addSubview(toView)
transitionContext.completeTransition(!wasCancelled)
})
}
Below is the screen shot

It seems Like you have taken UINavigationController for your second tab. But somehow your connection from UINavigationController to your secondViewController is lost. Please check the image of a storyboard which may be in your scenario.

I also get the same as a blank screen and I fixed it by making the current view it the initial view controller.
check is Initial View Controller

Related

How to avoid clipping when animating transform in viewController transitionAnimation

I have a problem with a custom animation viewController transition.
I have a collectionView (fromView) and when a cell is selected, it grows to fullSize (toView).
It's working great except for the bottom cells, they are cropped when animating to center.
I tried to change the frame of the containerView to get extra space to aboid the crop but it's messing up the positions of the viewcontrollers in it.
This is the relevant code for the transition and a sketch to spot the issue.
Many thanks.
Actual behaviour:
Expected:
// originFrame is define by the selected cell in fromView
var originFrame = CGRect.zero
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toView = transitionContext.view(forKey: .to),
let fromView = transitionContext.view(forKey: .from)
else { return }
let finalFrame = toView.frame
let growFactor = finalFrame.width / originFrame.width
let centerDiffX = fromView.center.x-originFrame.midX
let centerDiffY = fromView.center.y-originFrame.midY
// Animate the transition.
UIView.animate(
withDuration: duration,
delay:0.0,
animations: {
let scaleTransform = CGAffineTransform(scaleX: growFactor, y: growFactor)
fromView.transform = CGAffineTransform(translationX: deltaX, y: deltaY).concatenating(scaleTransform)
...
Make a snapshot of the collectionview do the trick ;)

Custom UINavigationController Animation Unresponsive When Finished

I'm trying to make a slide animation for my navigation controller transitions. For instance, when I push a VC, the presenting and the presented VCs will transition just like a UIPageViewController transition.
Here is what I've coded so far:
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to)
else { return }
let containerView = transitionContext.containerView
toView.frame = CGRect(x: -toViewFrameHorizationtalPosition,
y: 64,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height)
containerView.addSubview(fromView)
containerView.addSubview(toView)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: .curveEaseInOut,
animations: {
containerView.frame.origin.x = self.toViewFrameHorizationtalPosition
}) { completed in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
The transition animation is happening as I expected it to be but even though against various tries, once the animation executes the view becomes unresponsive; it does not recognise any touch events.
The navigation bar works fine but when I switch back to the initial view, that view is unresponsive as well.
I've read a lot on this and been struggling with this for a while now although I can't seem to spot the issue. Any ideas?
Alright then, I finally found the solution. Many many thanks to #DonMag for pointing me in the right direction with his great comment!
First off, as #DonMag has also mentioned, it was needless of me to add the fromView into the containerView as it's already present inside there.
Anyways, the problem with the presented view, toView, being unresponsive was that for the purposes of making the slide animation:
I was changing the x value of the toViews frame origin to position it right inside the container view,
and moving the container view again by changing it's x value of it's frame origin. I'm moving the container view because I want both the fromView and the toView to act one next to the other, i.e. just like how a UIPageViewController animates.
The problem was that I was not setting them back to their original values. Once I set toViews and containerViews frame origins back to their original values in the animation completion block, everything worked as expected.
Here is the revised animation code:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toView = transitionContext.view(forKey: .to)
else { return }
toView.removeFromSuperview()
toView.frame = CGRect(x: -toViewFrameHorizationtalPosition,
y: 64,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height)
let containerView = transitionContext.containerView
containerView.addSubview(toView)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: .curveEaseInOut,
animations: {
containerView.frame.origin.x = self.toViewFrameHorizationtalPosition
}) { completed in
containerView.frame.origin.x = 0
toView.frame.origin.x = 0
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
P.S. Again, many many thanks to #DonMag. Your insight has been really helpful.

How to include an ImageView into a Custom Segue?

I'm attempting to create a custom segue where the order of the animation goes
1st UIViewController -> ImageView -> 2nd UIViewController
The code I have works, but for some reason the ImageView is black. I've tried using various images and literal image sources, but it's still black.
Here's some pictures for a better understanding-
1 (As the first UIViewController is replaced by the ImageView)
2 (As the ImageView is replaced by the second UIViewController)
/**
This segue transitions by dragging the destination UIViewController in from the right and replacing the old UIViewController by pushing it off to the left. This Segue also contains an ImageView that is between the two UIViewControllers.
*/
class CustomSegue2: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let firstView = self.source.view as UIView!
let secondView = UIImageView()
let thirdView = self.destination.view as UIView!
// Get the screen width and height.
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
// Set properties of secondView
secondView.frame = CGRect(x: width, y: 0, width: width, height: height)
secondView.contentMode = UIViewContentMode.scaleToFill
secondView.image = #imageLiteral(resourceName: "orangeRedGradient_MESH_tealGreenGradient")
// Specify the initial position of the destination view.
let rect = CGRect(x: width + width, y: 0.0, width: width, height: height)
thirdView?.frame = rect
// Access the app's key window and insert the destination view above the current
let window = UIApplication.shared.keyWindow
window?.insertSubview(thirdView!, aboveSubview: firstView!)
// Animation the transition.
UIView.animate(withDuration: 10, animations: { () -> Void in
firstView?.frame = firstView!.frame.offsetBy(dx: -width-width, dy: 0.0)
secondView.frame = secondView.frame.offsetBy(dx: -width-width, dy: 0.0)
thirdView?.frame = thirdView!.frame.offsetBy(dx: -width-width, dy: 0.0)
}) { (Finished) -> Void in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
secondView never appears because at no time do you insert it into the interface.
(I would describe your code overall as completely wrong-headed — if you want a custom transition, you should be writing proper custom transition animation code. But that answers the question of why the image view is "black" — it isn't black, it is completely absent.)

slide transition using UIView.transition() method

I have and table view and when the user clicks a button I want it to reload it's data and change numerous other things. Sadly, my users have not been satisfied with the available options for the transitions/animations, and wanted a sliding transition. I couldn't find anything online to do this, however. Here is my code:
UIView.transition(with: view, duration: 0.5,options: .transitionCurlUp,animations:
{ () -> Void in
self.schedule.reloadData()
self.formatter.dateFormat = "yyyy-MM-dd"
let dayOfWeek = self.week[self.getDayOfWeek(self.currentDate)!-1]
var forReturn = dayOfWeek.getCourseNumber()
if forReturn>3{
forReturn = forReturn + 1
self.refreshButton.isHidden = false
self.noClasses.isHidden = true
}else{
self.refreshButton.isHidden = true
self.noClasses.isHidden = false
}
self.changeWeek()
let startDate = self.formatter.string(from: self.startDateBase)
if self.currentDate == startDate{
self.other1.text = "Today's Schedule"
}else{
self.formatter.dateFormat = "yyyy-MM-dd"
self.currentDate = self.formatter.string(from: self.date)
self.other1.text = dayNames[self.getDayOfWeek(self.currentDate)!-1] + " Schedule"
}
}, completion: nil
);
Thank you.
You can go old school on this:
take a snapshot of the view;
add it to the view hierarchy;
apply offsetting transforms to the snapshot view and the view, itself;
animate the restoration of the main view's transform back to .identity; and
removing the snapshot view when done with the animation.
Thus:
let snapshotView = snapshot()
view.addSubview(snapshotView)
snapshotView.transform = CGAffineTransform(translationX: -view.bounds.size.width, y: 0)
view.transform = CGAffineTransform(translationX: view.frame.size.width, y: 0)
// do whatever updates to the views you want here
tableView.reloadData()
UIView.animate(withDuration: 0.5, animations: {
self.view.transform = .identity
}, completion: { _ in
snapshotView.removeFromSuperview()
})
For the snapshot, I used:
func snapshot() -> UIView {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, true, 0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let imageView = UIImageView(image: image)
imageView.frame = view.bounds
return imageView
}
By the way, you can also use snapshotView(afterScreenupdates:), which is faster than drawHierarchy, but it doesn't work in 7+ simulator for some reason (see https://forums.developer.apple.com/thread/63438), so I'm not 100% comfortable with it. But if you wanted to do that, you'd do replace the shapshotView declaration with:
let snapshotView = view.snapshotView(afterScreenUpdates: false)!

How to use UIViewPropertyAnimator to rotate 360º

If I want to take advantage of user-interactive animations via UIViewPropertyAnimator how do I animate an object 360º clockwise?
// Does not animate
let animator = UIViewPropertyAnimator(duration: 2, curve: .linear) {
let radians = Angle(360).radians // 6.28318530717959
view.transform = view.transform.rotated(by: CGFloat(radians))
}
// (Using RxSwift to handle the user interaction via UISlider,
// but that can be ignored as an implementation detail for this question.)
let scrubs = slider.rx.value.asDriver()
scrubs.drive(onNext: { (value:Float) -> Void in
animator.fractionComplete = CGFloat(value)
})
Fortunately, UIViewPropertyAnimator makes this very simple (if not slightly hackish?). Just make two animations, each for 180º.:
let animator = UIViewPropertyAnimator(duration: 2, curve: .linear) {
let radians = Angle(180).radians // 3.14159265358979
view.transform = view.transform.rotated(by: CGFloat(radians))
}
animator.addAnimations {
let radians = Angle(180).radians
view.transform = view.transform.rotated(by: radians)
}
This will act like one animation which can be started, stopped, paused, finished or "scrubbed" via fractionComplete.

Resources