Skip to content

Commit

Permalink
Merge pull request #40 from batuhansk/fix-memory-leak
Browse files Browse the repository at this point in the history
fix memory leak
  • Loading branch information
ergunemr authored Sep 27, 2021
2 parents 35c6b3c + 06b4cab commit 23e3ce5
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 149 deletions.
2 changes: 1 addition & 1 deletion BottomPopup.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "BottomPopup"
s.version = "0.7.0"
s.version = "0.7.1"
s.summary = "BottomPopup provides a popup-like presentation style to any view controller"

s.homepage = "https://github.com/ergunemr/BottomPopup"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class BottomPopupDismissAnimator: NSObject, UIViewControllerAnimatedTransi
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return attributesOwner.popupDismissDuration
attributesOwner.popupDismissDuration
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,29 @@ protocol BottomPopupDismissInteractionControllerDelegate: class {
}

final class BottomPopupDismissInteractionController: UIPercentDrivenInteractiveTransition {
private let kMinPercentOfVisiblePartToCompleteAnimation = CGFloat(0.5)
private let kSwipeDownThreshold = CGFloat(1000)
private weak var presentedViewController: BottomPresentableViewController?
private weak var delegate: BottomPopupDismissInteractionControllerDelegate?
private weak var transitioningDelegate: BottomPopupTransitionHandler?
private unowned var attributesDelegate: BottomPopupAttributesDelegate
weak var delegate: BottomPopupDismissInteractionControllerDelegate?
private (set) var isInteractiveDismissStarted: Bool = false

private var currentPercent: CGFloat = 0 {
didSet {
delegate?.dismissInteractionPercentChanged(from: oldValue, to: currentPercent)
}
}

init(presentedViewController: BottomPresentableViewController?, attributesDelegate: BottomPopupAttributesDelegate) {
init(presentedViewController: BottomPresentableViewController?, delegate: BottomPopupDismissInteractionControllerDelegate?, attributesDelegate: BottomPopupAttributesDelegate) {
self.presentedViewController = presentedViewController
self.transitioningDelegate = presentedViewController?.transitioningDelegate as? BottomPopupTransitionHandler
self.delegate = delegate
self.attributesDelegate = attributesDelegate
super.init()
preparePanGesture(in: presentedViewController?.view)
}

private func finishAnimation(withVelocity velocity: CGPoint) {
if currentPercent > kMinPercentOfVisiblePartToCompleteAnimation || velocity.y > kSwipeDownThreshold {
if currentPercent > BottomPopupConstants.minPercentOfVisiblePartToCompleteAnimation || velocity.y > BottomPopupConstants.swipeDownThreshold {
finish()
} else {
cancel()
Expand All @@ -55,13 +55,13 @@ final class BottomPopupDismissInteractionController: UIPercentDrivenInteractiveT

switch pan.state {
case .began:
transitioningDelegate?.isInteractiveDismissStarted = true
isInteractiveDismissStarted = true
presentedViewController?.dismiss(animated: true, completion: nil)
case .changed:
update(currentPercent)
default:
let velocity = pan.velocity(in: presentedViewController?.view)
transitioningDelegate?.isInteractiveDismissStarted = false
isInteractiveDismissStarted = false
finishAnimation(withVelocity: velocity)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import UIKit

open class BottomPopupNavigationController: UINavigationController, BottomPopupAttributesDelegate {

private var transitionHandler: BottomPopupTransitionHandler?
open weak var popupDelegate: BottomPopupDelegate?

Expand All @@ -29,8 +28,7 @@ open class BottomPopupNavigationController: UINavigationController, BottomPopupA

open override func viewDidLoad() {
super.viewDidLoad()

transitionHandler?.notifyViewLoaded(withPopupDelegate: popupDelegate)
transitionHandler?.notifyViewLoaded()
popupDelegate?.bottomPopupViewLoaded()
self.view.accessibilityIdentifier = popupViewAccessibilityIdentifier
}
Expand Down Expand Up @@ -63,7 +61,7 @@ open class BottomPopupNavigationController: UINavigationController, BottomPopupA
//MARK: Private Methods

private func initialize() {
transitionHandler = BottomPopupTransitionHandler(popupViewController: self)
transitionHandler = BottomPopupTransitionHandler(popupViewController: self, popupDelegate: popupDelegate)
transitioningDelegate = transitionHandler
modalPresentationStyle = .custom
}
Expand All @@ -76,23 +74,15 @@ open class BottomPopupNavigationController: UINavigationController, BottomPopupA
self.view.layer.mask = maskLayer
}

//MARK: BottomPopupAttributesDelegate Variables

open var popupHeight: CGFloat { return BottomPopupConstants.kDefaultHeight }

open var popupTopCornerRadius: CGFloat { return BottomPopupConstants.kDefaultTopCornerRadius }

open var popupPresentDuration: Double { return BottomPopupConstants.kDefaultPresentDuration }

open var popupDismissDuration: Double { return BottomPopupConstants.kDefaultDismissDuration }

open var popupShouldDismissInteractivelty: Bool { return BottomPopupConstants.dismissInteractively }

open var popupDimmingViewAlpha: CGFloat { return BottomPopupConstants.kDimmingViewDefaultAlphaValue }

open var popupShouldBeganDismiss: Bool { return BottomPopupConstants.shouldBeganDismiss }

open var popupViewAccessibilityIdentifier: String { return BottomPopupConstants.defaultPopupViewAccessibilityIdentifier }
// MARK: - BottomPopupAttributesDelegate Variables
open var popupHeight: CGFloat { BottomPopupConstants.defaultHeight }
open var popupTopCornerRadius: CGFloat { BottomPopupConstants.defaultTopCornerRadius }
open var popupPresentDuration: Double { BottomPopupConstants.defaultPresentDuration }
open var popupDismissDuration: Double { BottomPopupConstants.defaultDismissDuration }
open var popupShouldDismissInteractivelty: Bool { BottomPopupConstants.dismissInteractively }
open var popupDimmingViewAlpha: CGFloat { BottomPopupConstants.dimmingViewDefaultAlphaValue }
open var popupShouldBeganDismiss: Bool { BottomPopupConstants.shouldBeganDismiss }
open var popupViewAccessibilityIdentifier: String { BottomPopupConstants.defaultPopupViewAccessibilityIdentifier }
}

extension BottomPopupNavigationController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class BottomPopupPresentAnimator: NSObject, UIViewControllerAnimatedTransi
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return attributesOwner.popupPresentDuration
attributesOwner.popupPresentDuration
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ final class BottomPopupPresentationController: UIPresentationController {
private unowned var attributesDelegate: BottomPopupAttributesDelegate

override var frameOfPresentedViewInContainerView: CGRect {
get {
return CGRect(origin: CGPoint(x: 0, y: UIScreen.main.bounds.size.height - popupHeight), size: CGSize(width: presentedViewController.view.frame.size.width, height: popupHeight))
}
CGRect(origin: CGPoint(x: 0, y: UIScreen.main.bounds.size.height - popupHeight), size: CGSize(width: presentedViewController.view.frame.size.width, height: popupHeight))
}

private func changeDimmingViewAlphaAlongWithAnimation(to alpha: CGFloat) {
Expand Down Expand Up @@ -49,6 +47,13 @@ final class BottomPopupPresentationController: UIPresentationController {
override func dismissalTransitionWillBegin() {
changeDimmingViewAlphaAlongWithAnimation(to: 0)
}

func setHeight(to height: CGFloat) {
popupHeight = height
UIView.animate(withDuration: attributesDelegate.popupPresentDuration) {
self.containerViewWillLayoutSubviews()
}
}

@objc private func handleTap(_ tap: UITapGestureRecognizer) {
guard attributesDelegate.popupShouldBeganDismiss else { return }
Expand All @@ -73,12 +78,3 @@ private extension BottomPopupPresentationController {
[tapGesture, swipeGesture].forEach { dimmingView.addGestureRecognizer($0) }
}
}

extension BottomPopupPresentationController {
func setHeight(to height: CGFloat) {
popupHeight = height
UIView.animate(withDuration: attributesDelegate.popupPresentDuration) {
self.containerViewWillLayoutSubviews()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,54 @@
import UIKit

final class BottomPopupTransitionHandler: NSObject, UIViewControllerTransitioningDelegate {
private weak var popupDelegate: BottomPopupDelegate?
private weak var popupViewController: BottomPresentableViewController?
private let presentAnimator: BottomPopupPresentAnimator
private let dismissAnimator: BottomPopupDismissAnimator
private var interactionController: BottomPopupDismissInteractionController?
private var bottomPopupPresentationController: BottomPopupPresentationController?
private unowned var popupViewController: BottomPresentableViewController
fileprivate weak var popupDelegate: BottomPopupDelegate?
var isInteractiveDismissStarted = false

init(popupViewController: BottomPresentableViewController) {
init(popupViewController: BottomPresentableViewController, popupDelegate: BottomPopupDelegate?) {
self.popupViewController = popupViewController

self.popupDelegate = popupDelegate
presentAnimator = BottomPopupPresentAnimator(attributesOwner: popupViewController)
dismissAnimator = BottomPopupDismissAnimator(attributesOwner: popupViewController)
}

//MARK: Public
func notifyViewLoaded(withPopupDelegate delegate: BottomPopupDelegate?) {
self.popupDelegate = delegate
if popupViewController.popupShouldDismissInteractivelty {
interactionController = BottomPopupDismissInteractionController(presentedViewController: popupViewController, attributesDelegate: popupViewController)
interactionController?.delegate = self
func notifyViewLoaded() {
if let popupViewController = popupViewController, popupViewController.popupShouldDismissInteractivelty {
interactionController = BottomPopupDismissInteractionController(presentedViewController: popupViewController, delegate: self, attributesDelegate: popupViewController)
}
}

func setHeight(to height: CGFloat) {
guard let presentationController = popupViewController?.presentationController as? BottomPopupPresentationController else { return }
presentationController.setHeight(to: height)
}

//MARK: Specific animators
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
bottomPopupPresentationController = BottomPopupPresentationController(presentedViewController: presented, presenting: presenting, attributesDelegate: popupViewController)
return bottomPopupPresentationController
guard let popupViewController = popupViewController else { return nil }
return BottomPopupPresentationController(presentedViewController: presented, presenting: presenting, attributesDelegate: popupViewController)
}

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return presentAnimator
presentAnimator
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return dismissAnimator
dismissAnimator
}

func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return isInteractiveDismissStarted ? interactionController : nil
guard let interactionController = interactionController, interactionController.isInteractiveDismissStarted else { return nil }
return interactionController
}
}

// MARK: - BottomPopupDismissInteractionControllerDelegate
extension BottomPopupTransitionHandler: BottomPopupDismissInteractionControllerDelegate {
func dismissInteractionPercentChanged(from oldValue: CGFloat, to newValue: CGFloat) {
popupDelegate?.bottomPopupDismissInteractionPercentChanged(from: oldValue, to: newValue)
}
}
extension BottomPopupTransitionHandler {
func setHeight(to height: CGFloat) {
bottomPopupPresentationController?.setHeight(to: height)
}
}
14 changes: 8 additions & 6 deletions BottomPopup/BottomPopupController/BottomPopupUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ public protocol BottomPopupAttributesDelegate: class {
var popupViewAccessibilityIdentifier: String { get }
}

public struct BottomPopupConstants {
static let kDefaultHeight: CGFloat = 377.0
static let kDefaultTopCornerRadius: CGFloat = 10.0
static let kDefaultPresentDuration = 0.5
static let kDefaultDismissDuration = 0.5
public enum BottomPopupConstants {
static let minPercentOfVisiblePartToCompleteAnimation: CGFloat = 0.5
static let swipeDownThreshold: CGFloat = 1000
static let defaultHeight: CGFloat = 377.0
static let defaultTopCornerRadius: CGFloat = 10.0
static let defaultPresentDuration = 0.5
static let defaultDismissDuration = 0.5
static let dismissInteractively = true
static let shouldBeganDismiss = true
static let kDimmingViewDefaultAlphaValue: CGFloat = 0.5
static let dimmingViewDefaultAlphaValue: CGFloat = 0.5
static let defaultPopupViewAccessibilityIdentifier: String = "bottomPopupView"
}
59 changes: 21 additions & 38 deletions BottomPopup/BottomPopupController/BottomPopupViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,86 +16,69 @@ open class BottomPopupViewController: UIViewController, BottomPopupAttributesDel

override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

initialize()
}

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

initialize()
}

open override func viewDidLoad() {

super.viewDidLoad()
transitionHandler?.notifyViewLoaded(withPopupDelegate: popupDelegate)
transitionHandler?.notifyViewLoaded()
popupDelegate?.bottomPopupViewLoaded()
self.view.accessibilityIdentifier = popupViewAccessibilityIdentifier
view.accessibilityIdentifier = popupViewAccessibilityIdentifier
}

open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

curveTopCorners()
popupDelegate?.bottomPopupWillAppear()
}

open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

popupDelegate?.bottomPopupDidAppear()
}

open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

popupDelegate?.bottomPopupWillDismiss()
}

open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)

popupDelegate?.bottomPopupDidDismiss()
}

// MARK: - Public Methods
open func updatePopupHeight(to height: CGFloat) {
transitionHandler?.setHeight(to: height)
}

//MARK: Private Methods

// MARK: Private Methods
private func initialize() {
transitionHandler = BottomPopupTransitionHandler(popupViewController: self)
transitionHandler = BottomPopupTransitionHandler(popupViewController: self, popupDelegate: popupDelegate)
transitioningDelegate = transitionHandler
modalPresentationStyle = .custom
}

private func curveTopCorners() {
let path = UIBezierPath(roundedRect: self.view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: popupTopCornerRadius, height: 0))
let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: popupTopCornerRadius, height: 0))
let maskLayer = CAShapeLayer()
maskLayer.frame = self.view.bounds
maskLayer.frame = view.bounds
maskLayer.path = path.cgPath
self.view.layer.mask = maskLayer
view.layer.mask = maskLayer
}

//MARK: BottomPopupAttributesDelegate Variables

open var popupHeight: CGFloat { return BottomPopupConstants.kDefaultHeight }

open var popupTopCornerRadius: CGFloat { return BottomPopupConstants.kDefaultTopCornerRadius }

open var popupPresentDuration: Double { return BottomPopupConstants.kDefaultPresentDuration }

open var popupDismissDuration: Double { return BottomPopupConstants.kDefaultDismissDuration }

open var popupShouldDismissInteractivelty: Bool { return BottomPopupConstants.dismissInteractively }

open var popupDimmingViewAlpha: CGFloat { return BottomPopupConstants.kDimmingViewDefaultAlphaValue }

open var popupShouldBeganDismiss: Bool { return BottomPopupConstants.shouldBeganDismiss }

open var popupViewAccessibilityIdentifier: String { return BottomPopupConstants.defaultPopupViewAccessibilityIdentifier }
}

extension BottomPopupViewController {
open func updatePopupHeight(to height: CGFloat) {
transitionHandler?.setHeight(to: height)
}
// MARK: - BottomPopupAttributesDelegate Variables
open var popupHeight: CGFloat { BottomPopupConstants.defaultHeight }
open var popupTopCornerRadius: CGFloat { BottomPopupConstants.defaultTopCornerRadius }
open var popupPresentDuration: Double { BottomPopupConstants.defaultPresentDuration }
open var popupDismissDuration: Double { BottomPopupConstants.defaultDismissDuration }
open var popupShouldDismissInteractivelty: Bool { BottomPopupConstants.dismissInteractively }
open var popupDimmingViewAlpha: CGFloat { BottomPopupConstants.dimmingViewDefaultAlphaValue }
open var popupShouldBeganDismiss: Bool { BottomPopupConstants.shouldBeganDismiss }
open var popupViewAccessibilityIdentifier: String { BottomPopupConstants.defaultPopupViewAccessibilityIdentifier }
}
Loading

0 comments on commit 23e3ce5

Please sign in to comment.