UIVisualEffectView creating unwanted shadow while presenting new view - swift

In my custom presentation transition I've created a new view controller which will pre presented on top of the current active view controller (see screenshot). Somehow there's a shadow behind the blue view controller and I have no idea where it's coming from. Is there a way to stop getting that shadow?
The project is completely empty and has only 2 empty view controllers.
This is the code I'm using:
class ViewController: UIViewController {
let transitionDelegate = TransitionManager()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellowColor()
let button = UIButton(type: .System)
button.frame = CGRectMake(10, 10, 50, 50)
button.addTarget(self, action: "test:", forControlEvents: .TouchUpInside)
button.backgroundColor = UIColor.redColor()
view.addSubview(button)
}
func test(sender: UIButton) {
let destination = UIViewController()
destination.view.backgroundColor = .blueColor()
destination.transitioningDelegate = transitionDelegate
destination.modalPresentationStyle = .Custom
presentViewController(destination, animated: true, completion: nil)
}
}
The code for presenting the view:
class PresentingTransition: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.3
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let presented = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let container = transitionContext.containerView()!
let durations = transitionDuration(transitionContext)
presented.view.alpha = 0
container.addSubview(presented.view)
UIView.animateWithDuration(durations, animations: { presented.view.alpha = 1 }) { transitionContext.completeTransition($0) }
}
}
The code for handling the presenting view controller:
class PresentationController: UIPresentationController {
var background: UIView!
override init(presentedViewController: UIViewController, presentingViewController: UIViewController) {
super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
prepareBackground()
}
func prepareBackground() {
self.background = UIView(frame: presentingViewController.view.bounds)
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blur.frame = background.bounds
blur.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
background.addSubview(blur)
let tapRecognizer = UITapGestureRecognizer(target: self, action: "backgroundTapped:")
background.addGestureRecognizer(tapRecognizer)
}
func backgroundTapped(tapRecognizer: UITapGestureRecognizer) {
presentingViewController.dismissViewControllerAnimated(true, completion: nil)
}
override func presentationTransitionWillBegin() {
let container = containerView!
background.frame = container.bounds
background.alpha = 0.0
container.insertSubview(background, atIndex: 0)
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in self.background.alpha = 1.0 }, completion: nil)
}
override func dismissalTransitionWillBegin() {
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in self.background.alpha = 0.0 }, completion: nil)
}
override func frameOfPresentedViewInContainerView() -> CGRect {
return containerView!.bounds.insetBy(dx: 100, dy: 100)
}
override func containerViewWillLayoutSubviews() {
background.frame = containerView!.bounds
presentedView()!.frame = frameOfPresentedViewInContainerView()
}
}

Related

How to present image in full screen in UIPageViewController?

I'm trying to present full images in UIPageViewController. Each image is in separate view controller. I set contentMode to scaleAspectFill. The problem is that when I scroll back, two images merge like this https://i.imgur.com/CSgJLWq.png. I also tried to give width to every image but nothing changes
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "onboarding-concert")
imageView.contentMode = .scaleAspectFill
//imageView.frame.size.width = 400
return imageView
}()
private func setUI() {
view.addSubview(imageView)
imageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
view.addSubview(label)
label.snp.makeConstraints { make in
make.top.equalToSuperview().inset(90)
make.left.equalToSuperview().inset(40)
}
}
UIPageViewController code
class OnboardingViewController: UIPageViewController {
var pages = [UIViewController]()
let pageControl = UIPageControl()
let initialPage = 0
var pageControlBottomAnchor: NSLayoutConstraint?
override func viewDidLoad() {
setup()
style()
layout()
}
func setup() {
dataSource = self
delegate = self
pageControl.addTarget(self, action: #selector(pageControlTapped(_:)), for: .valueChanged)
let page1 = FirstPageViewController()
let page2 = SecondPageViewController()
let page3 = ThirdPageViewController()
pages.append(page1)
pages.append(page2)
pages.append(page3)
setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
}
func style() {
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = .black
pageControl.pageIndicatorTintColor = .systemGray2
pageControl.numberOfPages = pages.count
pageControl.currentPage = initialPage
pageControl.isUserInteractionEnabled = false
}
func layout() {
view.addSubview(pageControl)
pageControl.snp.makeConstraints { make in
make.width.equalToSuperview()
make.height.equalTo(20)
make.centerX.equalToSuperview()
}
pageControlBottomAnchor = view.bottomAnchor.constraint(equalToSystemSpacingBelow: pageControl.bottomAnchor, multiplier: 2)
pageControlBottomAnchor?.isActive = true
}
#objc func pageControlTapped(_ sender: UIPageControl) {
setViewControllers([pages[sender.currentPage]], direction: .forward, animated: true, completion: nil)
}
}

Removing Animation From UINavigationController Pushed Unwind?

I have a simple push segue from one view controller to another. I want both segues (original and unwind to be unanimated).
In the attached playground, specifying false for the segue does, indeed remove the PUSH animation, but not the UNWIND animation.
Is there a way to remove the implicit animation in the UNWIND segue?
import UIKit
import PlaygroundSupport
class SourceViewController : UIViewController {
#objc func goDestination(_: Any) {
navigationController?.pushViewController(DestinationViewController(), animated: false)
}
override func loadView() {
view = UIView()
navigationItem.title = "SOURCE"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(goDestination(_:)))
}
}
class DestinationViewController : UIViewController {
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
navigationItem.title = "DESTINATION"
}
}
PlaygroundPage.current.liveView = UINavigationController(rootViewController: SourceViewController())
Those animations are fully customizable from the UINavigationControllerDelegate, check out the first "Supporting Custom Transition Animations" method
https://developer.apple.com/documentation/uikit/uinavigationcontrollerdelegate
Some details here https://www.youtube.com/watch?v=jWckfDNUJVY
at 23 minutes.
I'm greenchecking #glotcha's answer. It led me to this Medium post, which led me to this solution (I really wanted a fade transition):
import UIKit
import PlaygroundSupport
class TransitioningAnimatorInOut: NSObject, UIViewControllerAnimatedTransitioning {
var presenting: Bool = false
func transitionDuration(using: UIViewControllerContextTransitioning?) -> TimeInterval { 0.75 }
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to) else { return }
let container = transitionContext.containerView
if presenting {
container.addSubview(toView)
toView.alpha = 0.0
} else {
container.insertSubview(toView, belowSubview: fromView)
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
if self.presenting {
toView.alpha = 1.0
} else {
fromView.alpha = 0.0
}
}) { _ in
let success = !transitionContext.transitionWasCancelled
if !success {
toView.removeFromSuperview()
}
transitionContext.completeTransition(success)
}
}
init(presenting inPresenting: Bool) { presenting = inPresenting }
}
extension UINavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return TransitioningAnimatorInOut(presenting: true)
} else {
return TransitioningAnimatorInOut(presenting: false)
}
}
}
class SourceViewController : UIViewController {
#objc func goDestination(_: Any) {
navigationController?.pushViewController(DestinationViewController(), animated: true)
}
override func loadView() {
view = UIView()
view?.backgroundColor = .yellow
navigationItem.title = "SOURCE"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(goDestination(_:)))
}
}
class DestinationViewController : UIViewController {
override func loadView() {
view = UIView()
view?.backgroundColor = .red
navigationItem.title = "DESTINATION"
}
}
PlaygroundPage.current.liveView = UINavigationController(rootViewController: SourceViewController())

Problem with delegates removing annotation

I have two screens. The first one (firstViewController) has a mapView with a UITapGestureRecognizer. When the user taps the screen, an annotations is added to the map and the second screen (secondViewController) is presented.
When the user dismisses the secondViewController and comes back to the first one, the annotation should be removed. I know I have to use delegation, but I just can't make it to work.
This is the code I have now:
class firstViewController: UIViewController, AnnotationDelegate {
let mapView = MKMapView()
var temporaryPinArray = [MKPointAnnotation]()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
mapView.addGestureRecognizer(gesture)
secondVC.annotationDelegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
mapView.frame = view.bounds
}
#objc func handleTap(_ gestureReconizer: UILongPressGestureRecognizer) {
let location = gestureReconizer.location(in: mapView)
let coordinates = mapView.convert(location, toCoordinateFrom: mapView)
mapView.removeAnnotations(mapView.annotations)
let pin = MKPointAnnotation()
pin.coordinate = coordinates
temporaryPinArray.removeAll()
temporaryPinArray.append(pin)
mapView.addAnnotations(temporaryPinArray)
// Present secondViewController
let secondVC = SecondViewController()
panel.set(contentViewController: secondVC)
panel.addPanel(toParent: self)
}
func didRemoveAnnotation(annotation: MKPointAnnotation) {
mapView.removeAnnotation(annotation)
}
}
Second View Controller
protocol AnnotationDelegate {
func didRemoveAnnotation(annotation: [MKPointAnnotation])
}
class SecondViewController: UIViewController {
var annotationDelegate: AnnotationDelegate!
let mainVC = firstViewController()
let closeButton: UIButton = {
let button = UIButton()
button.backgroundColor = .grey
button.layer.cornerRadius = 15
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(closeButton)
closeButton.addTarget(self, action: #selector(dismissPanel), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
closeButton.frame = CGRect(x: view.frame.width-50, y: 10, width: 30, height: 30)
}
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation(annotation: mainVC.temporaryPinArray)
}
}
Thank you so much for your help!
You created a new instance of firstViewController inside SecondViewController. This instance is unrelated to the actual first one:
let mainVC = firstViewController()
This means that temporaryPinArray is different as well. So instead of passing in this unrelated array...
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation(annotation: mainVC.temporaryPinArray)
}
Just change the function to take no parameters instead:
protocol AnnotationDelegate {
func didRemoveAnnotation() /// no parameters
}
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation() /// no parameters
}
And inside firstViewController's didRemoveAnnotation, reference the actual temporaryPinArray.
func didRemoveAnnotation() {
mapView.removeAnnotations(temporaryPinArray) /// the current array
}

In keeping with the recent questions on closures used to pass data between VCs, how would I do the same when using a containerVC within the rootVC?

I saw a recent bountied question (can find the link if you wish to see it) about using closures to pass data between VCs where one VC was embedded in a navigation controller. While the use of a closure there was fairly easy since there was a direct point of contact between the two VCs (in the form a segue), I have been wondering how the same would work if this was not the case.
As an example, consider the following set up (similar to the OG question that inspired this post):
RootVC, which has a counter UILabel
A subContainer VC which takes up the lower half of RootVC, which has a button, pressing which should increment the UILabel on RootVC by one.
I have prepared the code as follows (with some code taken from the OG question):
RootVC:
class RootVC: UIViewController {
var tappedCount: Int = 0
let pagingContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var label: UILabel = {
let label = UILabel()
label.text = "\(tappedCount)"
label.textAlignment = .center
label.font = UIFont(name: "Copperplate", size: 90)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
view.addSubview(pagingContainer)
pagingContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
pagingContainer.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true
pagingContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
pagingContainer.heightAnchor.constraint(equalToConstant: 500).isActive = true
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
addChild(pageController)
pageController.didMove(toParent: self)
pageController.view.translatesAutoresizingMaskIntoConstraints = false
pagingContainer.addSubview(pageController.view)
pageController.view.heightAnchor.constraint(equalTo: pagingContainer.heightAnchor, multiplier: 1).isActive = true
pageController.view.widthAnchor.constraint(equalTo: pagingContainer.widthAnchor, multiplier: 1).isActive = true
pageController.view.topAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
pageController.view.bottomAnchor.constraint(equalTo: pagingContainer.bottomAnchor).isActive = true
pageController.view.leadingAnchor.constraint(equalTo: pagingContainer.leadingAnchor).isActive = true
pageController.view.trailingAnchor.constraint(equalTo: pagingContainer.trailingAnchor).isActive = true
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
}
}
SubContainerVC:
class SubContainerVC: UIViewController {
var callback : (() -> Void)?
let button: UIButton = {
let button = UIButton()
button.setTitle("Button!", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.backgroundColor = .green
return button
}()
#objc func buttonPressed(_ sender: UIButton) {
print("Hello")
//Pressing this button should increment the label on RootVC by one.
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
And the PageViewController swift file:
class PageViewController: UIPageViewController {
lazy var subViewControllers:[UIViewController] = {
return [SubContainerVC()]
}()
init(transitionStyle style:
UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
setViewControllerFromIndex(index: 0)
}
func setViewControllerFromIndex(index:Int) {
self.setViewControllers([subViewControllers[index]], direction: UIPageViewController.NavigationDirection.forward, animated: true, completion: nil)
}
}
extension PageViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return subViewControllers.count
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
if currentIndex <= 0 {
return nil
}
return subViewControllers[currentIndex-1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
if currentIndex >= subViewControllers.count-1 {
return nil
}
return subViewControllers[currentIndex+1]
}
}
You can inject the closure downstream to SubContainerVC, this will result in the closure execution coming up upstream.
Something along the lines (kept only the relevant VC code):
class SubContainerVC {
var buttonCallback: () -> Void = { }
#objc func buttonPressed(_ sender: UIButton) {
print("Hello")
buttonCallback()
}
}
class PageViewController: UIViewController {
// Note that you don't need the extra closure call for lazy vars
lazy var subViewControllers = [SubContainerVC()] {
didSet {
// just in case the controllers might change later on
subViewControllers.forEach { $0.buttonCallback = buttonCallback }
}
}
var buttonCallback: () -> Void = { } {
didSet {
subViewControllers.forEach { $0.buttonCallback = buttonCallback }
}
}
}
class RootVC: UIViewController {
var tappedCount: Int = 0 {
didSet {
label.text = "\(tappedCount)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
// this will trigger the `didSet` from PageViewController, resulting
// in the callback being propagated downstream
pageController.buttonCallback = { self.tappedCount += 1 }
}
}

How to display a PopoverViewController from a custom Navigation Bar Right Button?

I created a custom Navigation Bar class as illustrated below, which I used across multiple ViewControllers:
import UIKit
import ChameleonFramework
class CustomUINavigationBar: UINavigationBar {
let navigationBarRightButtonView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(rightNavBarButtonTitleForNormalState: String, rightNavBarButtonImageForNormalState: String, rightNavBarButtonImageForHighlightedState: String, rightNavBarButtonTarget: Any?, rightNavBarButtonSelector: Selector, isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String, normalStateNavBarLeftButtonImage: String, highlightedStateNavBarLeftButtonImage: String, navBarLeftButtonTarget: Any?, navBarLeftButtonSelector: Selector, labelTitleText: String, titleLabelFontHexColourCode: String, labelTitleFontSize: CGFloat, labelTitleFontType: String) {
self.init()
addNavBarRightButton(rightNavBarButtonTitleForNormalState: rightNavBarButtonTitleForNormalState, rightNavBarButtonImageForNormalState: rightNavBarButtonImageForNormalState, rightNavBarButtonImageForHighlightedState: rightNavBarButtonImageForHighlightedState, rightNavBarButtonTarget: rightNavBarButtonTarget, rightNavBarButtonSelector: rightNavBarButtonSelector)
addNavBarLeftButton(normalStateNavBarLeftButtonImage: normalStateNavBarLeftButtonImage, highlightedStateNavBarLeftButtonImage: highlightedStateNavBarLeftButtonImage, navBarLeftButtonTarget: navBarLeftButtonTarget, navBarLeftButtonSelector: navBarLeftButtonSelector)
setupNavigationBarEssentials(isNavBarTranslucent: isNavBarTranslucent, navBarBackgroundColourHexCode: navBarBackgroundColourHexCode, navBarBackgroundColourAlphaValue: navBarBackgroundColourAlphaValue, navBarStyle: navBarStyle, preferLargeTitles: preferLargeTitles, navBarDelegate: navBarDelegate, navBarItemsHexColourCode: navBarItemsHexColourCode)
addTitleLabel(labelTitleText: labelTitleText, titleLabelFontHexColourCode: titleLabelFontHexColourCode, labelTitleFontSize: labelTitleFontSize, labelTitleFontType: labelTitleFontType)
}
let customNavigationBarItem = UINavigationItem()
func addTitleLabel(labelTitleText titleText: String, titleLabelFontHexColourCode hexCode: String, labelTitleFontSize fontSize: CGFloat, labelTitleFontType fontType: String) {
let navBarTitle = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: 44))
navBarTitle.text = titleText
navBarTitle.textColor = UIColor(hexString: hexCode)
navBarTitle.textAlignment = .center
navBarTitle.font = UIFont(name: fontType, size: fontSize)
navBarTitle.numberOfLines = 0
navBarTitle.lineBreakMode = .byWordWrapping
customNavigationBarItem.titleView = navBarTitle
}
func addNavBarLeftButton(normalStateNavBarLeftButtonImage: String, highlightedStateNavBarLeftButtonImage: String, navBarLeftButtonTarget: Any?, navBarLeftButtonSelector: Selector) {
let navBarLeftButton: UIButton = {
let button = UIButton()
let normalStateNavBarLeftButtonImage = UIImage(named: normalStateNavBarLeftButtonImage)
let highlightedStateNavBarLeftButtonImage = UIImage(named: highlightedStateNavBarLeftButtonImage)
button.setImage(normalStateNavBarLeftButtonImage, for: .normal)
button.setImage(highlightedStateNavBarLeftButtonImage, for: .highlighted)
button.addTarget(navBarLeftButtonTarget, action: navBarLeftButtonSelector, for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let navBarLeftView: UIView = {
let view = UIView()
view.addSubview(navBarLeftButton)
NSLayoutConstraint.activate([
navBarLeftButton.topAnchor.constraint(equalTo: view.topAnchor),
navBarLeftButton.rightAnchor.constraint(equalTo: view.rightAnchor),
navBarLeftButton.bottomAnchor.constraint(equalTo: view.bottomAnchor),
navBarLeftButton.leftAnchor.constraint(equalTo: view.leftAnchor)
])
return view
}()
let navBarLeftButtonItem = UIBarButtonItem(customView: navBarLeftView)
customNavigationBarItem.leftBarButtonItem = navBarLeftButtonItem
}
func addNavBarRightButton(rightNavBarButtonTitleForNormalState: String, rightNavBarButtonImageForNormalState: String, rightNavBarButtonImageForHighlightedState: String, rightNavBarButtonTarget: Any?, rightNavBarButtonSelector: Selector) {
rightNavigationBarDropDownButton.setTitle(rightNavBarButtonTitleForNormalState, for: .normal)
rightNavigationBarDropDownButton.setImage(UIImage(named: rightNavBarButtonImageForNormalState), for: .normal)
rightNavigationBarDropDownButton.setTitleColor(.black, for: .normal)
rightNavigationBarDropDownButton.setTitleColor(.blue, for: .highlighted)
rightNavigationBarDropDownButton.addTarget(rightNavBarButtonTarget, action: rightNavBarButtonSelector, for: .touchUpInside)
rightNavigationBarDropDownButton.translatesAutoresizingMaskIntoConstraints = false
navigationBarRightButtonView.addSubview(rightNavigationBarDropDownButton)
NSLayoutConstraint.activate([
rightNavigationBarDropDownButton.topAnchor.constraint(equalTo: navigationBarRightButtonView.topAnchor),
rightNavigationBarDropDownButton.rightAnchor.constraint(equalTo: navigationBarRightButtonView.rightAnchor),
rightNavigationBarDropDownButton.leftAnchor.constraint(equalTo: navigationBarRightButtonView.leftAnchor),
rightNavigationBarDropDownButton.bottomAnchor.constraint(equalTo: navigationBarRightButtonView.bottomAnchor)
])
let navigationBarRightViewitem = UIBarButtonItem(customView: navigationBarRightButtonView)
customNavigationBarItem.rightBarButtonItem = navigationBarRightViewitem
}
func setupNavigationBarEssentials(isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String) {
items = [customNavigationBarItem]
isTranslucent = isNavBarTranslucent
barTintColor = UIColor(hexString: navBarBackgroundColourHexCode, withAlpha: navBarBackgroundColourAlphaValue)
barStyle = navBarStyle
prefersLargeTitles = preferLargeTitles
delegate = navBarDelegate
tintColor = UIColor(hexString: navBarItemsHexColourCode)
translatesAutoresizingMaskIntoConstraints = false
}
}
I then created an instance from the above custom Navigation Bar class into the viewController where I would like the custom Navigation bar to show. Then I tried to present the PopoverViewController whenever the user taps on the NavBarRightButtonItem, however, nothing is showing up, could please someone help me figuring out where did I go wrong, thanks a lot?
import UIKit
class BlueBookUniversalBeamsVC: UIViewController, UINavigationBarDelegate, UIPopoverPresentationControllerDelegate {
lazy var navigationBar = CustomUINavigationBar(rightNavBarButtonTitleForNormalState: "Sort By:", rightNavBarButtonImageForNormalState: "pullDownButton", rightNavBarButtonImageForHighlightedState: "pullUpButton", rightNavBarButtonTarget: self, rightNavBarButtonSelector: #selector(navigationBarRightButtonPressed(sender:)), isNavBarTranslucent: false, navBarBackgroundColourHexCode: "#FFFFFF", navBarBackgroundColourAlphaValue: 1.0, navBarStyle: .black, preferLargeTitles: false, navBarDelegate: self, navBarItemsHexColourCode: "#FF4F40", normalStateNavBarLeftButtonImage: "normalStateBackButton", highlightedStateNavBarLeftButtonImage: "highlightedStateBackButton", navBarLeftButtonTarget: self, navBarLeftButtonSelector: #selector(navigationBarLeftButtonPressed(sender:)), labelTitleText: "Universal Beams (UB)", titleLabelFontHexColourCode: "#000000", labelTitleFontSize: 16, labelTitleFontType: "AppleSDGothicNeo-Light")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(navigationBar)
}
}
override func viewDidLayoutSubviews() {
setupConstraints()
}
#objc func navigationBarLeftButtonPressed(sender : UIButton) {
let viewControllerToGoTo = BlueBookTabController()
present(viewControllerToGoTo, animated: true, completion: nil)
}
#objc func navigationBarRightButtonPressed(sender : UIButton) {
let button = sender as? UIButton
let buttonFrame = button?.frame ?? CGRect.zero
let popoverContentController = self.storyboard?.instantiateViewController(withIdentifier: "PopoverViewController") as? PopoverViewController
popoverContentController?.modalPresentationStyle = .popover
if let popoverPresentationController = popoverContentController?.popoverPresentationController {
popoverPresentationController.permittedArrowDirections = .up
popoverPresentationController.sourceView = self.view
popoverPresentationController.sourceRect = buttonFrame
popoverPresentationController.delegate = self
if let popoverController = popoverContentController {
present(popoverController, animated: true, completion: nil)
}
}
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
}
func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return true
}
func position(for bar: UIBarPositioning) -> UIBarPosition {
return UIBarPosition.topAttached
}
func setupConstraints() {
NSLayoutConstraint.activate([
navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor),
navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor),
navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
}
}
I managed to sort out my problem using the below code, what confused me at the beginning is that I am using a Standalone NavigationBar rather than a NavigationBar Controller:
#objc func navigationBarRightButtonPressed(sender : UIButton) {
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let popOverViewController = storyboard.instantiateViewController(withIdentifier: "PopoverViewController")
popOverViewController.modalPresentationStyle = .popover
let popover = popOverViewController.popoverPresentationController!
popover.delegate = self
popover.permittedArrowDirections = .up
// The sourceView in the below code line represents the view containing the anchor rectangle for the popover:
popover.sourceView = navigationBar.navigationBarRightButtonView
// The sourceRect in the below code line represents The rectangle in the specified view in which to anchor the popover:
popover.sourceRect = navigationBar.navigationBarRightButtonView.bounds
present(popOverViewController, animated: true, completion:nil)
}