I have a scrollView and there is a textView inside it and there is a function that slide my scrollView(with animation) in my main vc also I have a button(inside the black circle) ,when I press it, my pop up viewcontroller opens then I choose a value for animation delay in my function.
When I press my play button (inside the red circle) , I want to dismiss my pop up vc and I want my function to run. See:
func startScrollSlideShow(sliderValue: Float) {
if (scrollView.contentOffset.y <= (scrollView.contentSize.height - scrollView.frame.size.height)){
//reach bottom
UIScrollView.animate(withDuration: TimeInterval(sliderValue), delay: 0.5, options: .allowAnimatedContent, animations: {
self.scrollView.contentOffset.y += 3.5
}) { (completion) in
self.startScrollSlideShow(sliderValue: Float(UserDefaults.standard.string(forKey: "sliderValue")!)!)
}
} else {
return
}
}
#objc func scrollTextView(){
self.present(SliderViewController(title: "Otomatik Kaydırma"), animated: true, completion: nil)
// self.startScrollSlideShow(sliderValue: Float(UserDefaults.standard.string(forKey: "sliderValue")!)!)
}
these are in my main vc not my pop up vc.
You can use the delegate pattern for this:
protocol PlayViewDelegate: class {
func playButtonPressed()
}
// Implement the protocol in your Main VC
extension MainViewController: PlayViewDelegate {
func playButtonPressed() {
// Play
}
}
// Add delegate to the alert like VC
class PlayViewController: UIViewController {
weak var delegate: PlayViewDelegate?
#IBAction
func playPressed() {
self.delegate?.playButtonPressed()
self.dismiss(animated: true, completion: nil)
}
}
Related
I have a lot of view controllers that use the same two functions that show and hide popup for me. Everytime I use them, I ask myself if it wouldn't be better to put them in a global class called PopupUtils for example and set the functions as static functions.
I did it and it worked but I'm not sure if it's a good thing to do because I have to pass to my function three arguments: the parent view controller, the child view controller and the popup_container view
Since it's all passed by val, is there not a problem with memory ? or any other problem I should be aware of ?
Here is my static class called Popup Utils
class PopupUtils {
static func showPopupView(parentViewController: UIViewController, childViewController: UIViewController, popupContainer: UIView) {
parentViewController.addChild(childViewController)
popupContainer.addSubview(childViewController.view)
childViewController.view.frame = popupContainer.bounds
childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
childViewController.didMove(toParent: parentViewController)
UIView.transition(with: popupContainer, duration: 0.2, options: .transitionCrossDissolve, animations: {
popupContainer.isHidden = false
})
}
static func removePopupView(childViewController: UIViewController, popupContainer: UIView){
// Remove pop up VC from children
childViewController.willMove(toParent: nil)
childViewController.view.removeFromSuperview()
childViewController.removeFromParent()
// Hide pop up container
popupContainer.isHidden = true
// Release language menu
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "releaseMenuSwipe"), object: nil)
}
}
It’s not really bad but how about an extension of UIViewController
extension UIViewController {
func showPopupView(childViewController: UIViewController, popupContainer: UIView) {
addChild(childViewController)
popupContainer.addSubview(childViewController.view)
childViewController.view.frame = popupContainer.bounds
childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
childViewController.didMove(toParent: self)
UIView.transition(with: popupContainer, duration: 0.2, options: .transitionCrossDissolve, animations: {
popupContainer.isHidden = false
})
}
func removePopupView(childViewController: UIViewController, popupContainer: UIView) {
// Remove pop up VC from children
childViewController.willMove(toParent: nil)
childViewController.view.removeFromSuperview()
childViewController.removeFromParent()
// Hide pop up container
popupContainer.isHidden = true
// Release language menu
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "releaseMenuSwipe"), object: nil)
}
}
An alternative to get rid of the parameters is a protocol extension. It assumes that the adopting UIViewController has two properties popupContainer and childViewController, if they are optional change and handle the type accordingly.
The two methods in the extension are available for any UIViewController which adopts the protocol
protocol PopupManageable {
var popupContainer: UIView { get }
var childViewController: UIViewController { get }
}
extension PopupManageable where Self : UIViewController {
func showPopupView() {
self.addChild(childViewController)
popupContainer.addSubview(childViewController.view)
childViewController.view.frame = popupContainer.bounds
childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
childViewController.didMove(toParent: self)
UIView.transition(with: popupContainer, duration: 0.2, options: .transitionCrossDissolve, animations: {
self.popupContainer.isHidden = false
})
}
func removePopupView() {
// Remove pop up VC from children
childViewController.willMove(toParent: nil)
childViewController.view.removeFromSuperview()
childViewController.removeFromParent()
// Hide pop up container
popupContainer.isHidden = true
// Release language menu
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "releaseMenuSwipe"), object: nil)
}
}
I think you have to keep everything tracked. You are passing the view controller around and adding a child view controller. This may lead to the memory leak if you are not aware of complexity in the future. Keep tracking allocation whenever you add new task. in it.
I am trying to use a custom transition to a full screen custom camera. When I do so using a slow fade animation it after completing the animation turns black.
The camera which at the beginning of the animation seems to work,
suddenly goes away leaving behind the black background.
How can I make the transition work correctly?
Code:
mainVC:
#objc func buttonUp(_ sender: UIButton) {
toCam.transform = CGAffineTransform.identity.scaledBy(x: 1, y: 1)
toCam.backgroundColor = .yellow
segue()
}
func segue() {
performSegue(withIdentifier: "GoToCam", sender: self)
}
Custom segue class:
class goToCamAnimCustom: UIStoryboardSegue {
override func perform() {
scale()
}
func scale() {
guard let destinationView = self.destination.view else {
// Fallback to no fading
self.source.present(self.destination, animated: false, completion: nil)
return
}
destinationView.alpha = 0
self.source.view?.addSubview(destinationView)
UIView.animate(withDuration: CATransaction.animationDuration(), animations: {
destinationView.alpha = 0.5
}, completion: { _ in
self.source.present(self.destination, animated: false, completion: nil)
})
}
}
I'm trying to create a basic animation. I need to touch on a button to hide or show.
I wrote this code to tap on the screen:
func visibleControlButton(_ sender: UITapGestureRecognizer) {
if (backButton!.isHidden) {
_UIButtonHiddenAnimation.hiddenAnimation(button: self.backButton!, hide: false)
} else {
_UIButtonHiddenAnimation.hiddenAnimation(button: self.backButton!, hide: true)
}
}
Definition _UIButtonHiddenAnimation:
class _UIButtonHiddenAnimation {
class func hiddenAnimation(button: UIButton, hide: Bool) {
UIView.animate(withDuration: 0.2,
animations: {
hide ? (button.alpha = 0) : (button.alpha = 1.0)
},
completion: {
finished in hide ? (button.isHidden = true) : (button.isHidden = false)
})
}
}
Animates just hide the button. How to make an animated appearance of a button?
The problem is that if the button is hidden, you are animating the alpha to 1 but we cannot see that — because the button is hidden! Then you set isHidden to false and the button jumps into view.
The solution: forget all about isHidden and change only the alpha — and then change your if test to match what you are doing with the button, i.e. simply test against its alpha value. Thus (neatening things up as we go):
class _UIButtonHiddenAnimation {
class func hiddenAnimation(button: UIButton, hide: Bool) {
UIView.animate(withDuration: 0.2, animations: {
button.alpha = hide ? 0 : 1.0
})
}
}
func visibleControlButton(_ sender: UITapGestureRecognizer) {
_UIButtonHiddenAnimation.hiddenAnimation(
button: self.backButton!, hide: backButton!.alpha > 0.1)
}
What I am trying to do is a custom animation of pushing ViewController from the left side.
I have created my custom transitioning delegate and I provide my custom animation, and everything works fine (new view slides from the left side).
The only problem is that push animation in iOS isn't only about sliding a view from the right side. The VC being obscured is also slightly moving in the same directions as the VC being pushed. Also, navigation bar kinda blinks. I can of course try to imitate this behaviour by guessing what the parameters should be (for example how much the VC being obscured moves on different iPhones), but maybe it is possible to find the values somewhere?
Help greatly appreciated.
I would create a UIViewControllerAnimatedTransitioning protocol abiding object
class CustomHorizontalSlideTransition: NSObject, UIViewControllerAnimatedTransitioning {
var operation: UINavigationControllerOperation = .Push
convenience init(operation: UINavigationControllerOperation) {
self.init()
self.operation = operation
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()
let disappearingVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let appearingVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let bounds = UIScreen.mainScreen().bounds
if self.operation == .Push {
appearingVC.view.frame = CGRectOffset(bounds, -bounds.size.height, 0)
containerView!.addSubview(disappearingVC.view)
containerView!.addSubview(appearingVC.view)
} else {
appearingVC.view.frame = bounds
disappearingVC.view.frame = bounds
containerView!.addSubview(appearingVC.view)
containerView!.addSubview(disappearingVC.view)
}
UIView.animateWithDuration(transitionDuration(transitionContext),
delay: 0.0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: { () -> Void in
if self.operation == .Push {
appearingVC.view.frame = bounds
} else {
disappearingVC.view.frame = CGRectOffset(bounds, -bounds.size.width, 0)
}
}) { (complete) -> Void in
transitionContext.completeTransition(true)
}
}
}
Then in your "From" and "To" view controllers, set the navigationController's delegate to self in view ViewDidAppear
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
navigationController?.delegate = self
}
The in both view controllers, override the following to provide a transitionAnimatedTransition delegate method and return the protocol abiding instance for your animation
override func transitionAnimatedTransition(operation: UINavigationControllerOperation) -> UIViewControllerAnimatedTransitioning? {
return CustomHorizontalSlideTransition(operation: operation)
}
I've a strange issue with the NavigationBar behind the Statusbar.
It only occurs when the default statusbar changes to an "active" statusbar like the one that appears during an active call or a wifi hotspot.
Before the "active" statusbar appears, it looks like this (which is perfectly fine):
When I enable the wifi hotspot it's still fine:
But when I disable the wifi hotspot or end a call the statusbar size shrinks back to its previous size but the ViewController (in this case a UITableViewController) doesn't move up again. It looks like it has a top margin of the statusbars' size. In addition the statusbar is transparent and I can see the background of the view controller below the actual table view controller.
Any ideas on this issue?
Update:
I figured out that it's because of a custom modal transition that I've implemented.
It should be a dissolve animation.
That's the code:
class DissolveTransition: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
// vars
private var duration: NSTimeInterval = 0.3
private var presenting = true
// MARK: - UIViewControllerAnimatedTransitioning
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return self.duration
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
if (destination?.isBeingPresented() == true) {
self.animatePresentation(transitionContext)
}
else {
self.animateDismissal(transitionContext)
}
}
private func animatePresentation(transitionContext: UIViewControllerContextTransitioning) {
self.animateDissolve(transitionContext)
}
private func animateDismissal(transitionContext: UIViewControllerContextTransitioning) {
self.presenting = false
self.animateDissolve(transitionContext)
}
private func animateDissolve(transitionContext: UIViewControllerContextTransitioning) {
let source = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let container = transitionContext.containerView()!
destination.beginAppearanceTransition(true, animated: true)
let snapshotFromView = source.view.snapshotViewAfterScreenUpdates(true)
// 1. adding real view at the bottom of the view hierarchy
if (self.presenting) {
container.addSubview(destination.view)
}
// 2. adding snapshot of previous view to view hierarchy
container.addSubview(snapshotFromView)
// 3. removing (fade) prev snapshot view and show real VC
UIView.animateWithDuration(self.duration, animations: {
snapshotFromView.alpha = 0.0
}, completion: { (finished) in
if (finished) {
snapshotFromView.removeFromSuperview()
container.bringSubviewToFront(destination.view)
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
destination.endAppearanceTransition()
})
}
// MARK: - UIViewControllerTransitioningDelegate
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
}
I found out that it was because of my custom modal transition that presented this view.
There is an odd bug in iOS that views inside the screen are not resized after the statusBar is changed. This also appears in many well-known Apps.
I fixed it by resizing the views when the statusbar-size changes.
Use the following code in your AppDelegate:
func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
if (newStatusBarFrame.size.height < 40) {
if let window = self.window, subviews = self.window?.subviews {
for view in subviews {
UIView.animateWithDuration(0.3, animations: {
view.frame = window.bounds
})
}
}
}
}