How to enable leftBarButtonItem from other ViewController? - ios

When I click the left bar button on MessageController I make it isEnabled to false. Then I present PopUpViewController and on button click the function removeAnimate() runs. And in that function I would like to set MessageController left bar button to isEnabled to true. I have tried but it does not work. Can someone help ?
class PopUpViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
self.showAnimate()
}
func showAnimate()
{
view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
UIView.animate(withDuration: 0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
});
}
func removeAnimate()
{
let messagView = MessageController()
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.view.removeFromSuperview()
messagView.navigationItem.leftBarButtonItem?.isEnabled = true
}
});
}
lazy var cancleButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Cancel", for: UIControlState())
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
button.tintColor = .white
button.addTarget(self, action: #selector(removeAnimate), for: .touchUpInside)
return button
}()
}
MessageViewController
class MessageController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(preformSettings))
}
func preformSettings(){
let popViewController = PopUpViewController()
self.addChildViewController(popViewController)
popViewController.view.frame = self.view.frame
self.view.addSubview(popViewController.view)
popViewController.didMove(toParentViewController: self)
navigationItem.leftBarButtonItem?.isEnabled = false
}
}

You may use protocols. Write a protocol
protocol PopViewControllerDelegate: class {
func enableBackButton()
}
In PopUpViewController make an object of this protocol.
class PopUpViewController: UIViewController {
var delegate: PopViewControllerDelegate?
....
func removeAnimate() {
...
delegate?.enableBackButton()
}
}
In the MessageController implement this protocol.
class MessageController: UITableViewController {
func preformSettings(){
let popViewController = PopUpViewController()
popViewController.delegate = self
self.addChildViewController(popViewController)
....
}
}
extension MessageController: PopViewControllerDelegate {
func enableBackButton() {
navigationItem.leftBarButtonItem?.isEnabled = true
}
}

Related

Disable button2 if button1 is not clicked

I want to disable the button2 if button1 is not clicked, and enable it when button1 is clicked. The problem is, the button2 is disable even I already clicked the button1. I don't receive any error, I'm using Swift4 in Xcode10.
Here is my code:
class ViewController: UIViewController {
#IBOutlet weak var btn1: UIButton!
#IBOutlet weak var btn2: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
intro()
btn1.setImage(UIImage(named: "checked.png"), for: .selected)
btn1.setImage(UIImage(named: "unchecked.png"), for: .normal)
}
#IBAction func button1(_ sender: UIButton) {
UIView.animate(withDuration: 0.5, delay: 0.1, options: .curveLinear, animations: {
sender.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}) { (success) in
UIView.animate(withDuration: 0.5, delay: 0.1, options: .curveLinear, animations: {
sender.isSelected = !sender.isSelected
sender.transform = .identity
}, completion: nil)
}
}
func intro() {
if btn1.isSelected == true {
btn2.isUserInteractionEnabled = true
} else {
btn2.isUserInteractionEnabled = false
}
}
}
I also tried this code:
func intro() {
if btn1.isTouchInside == true {
btn2.isUserInteractionEnabled = true
} else {
btn2.isUserInteractionEnabled = false
}
}
I think first you want button2 is disable and when you click on the button1 its should be enable.
For this make
override func viewDidLoad() {
super.viewDidLoad()
btn1.setImage(UIImage(named: "checked.png"), for: .selected)
btn1.setImage(UIImage(named: "unchecked.png"), for: .normal)
btn2.isenable = false
}
#IBAction func button1(_ sender: UIButton) {
if sender.isSelected == true
{
btn2.isenable = true
}
else
{
btn2.isenable = false
}
}
Add any of the below code in your viewDidLoad()
btn2.isEnabled = false
or
btn2.isUserInteractionEnabled = false
and change your button action as,
#IBAction func button1(_ sender: UIButton) {
if btn1.isSelected == true {
btn2.isUserInteractionEnabled = true
} else {
btn2.isUserInteractionEnabled = false
}
}

How to reload the data from UI picker view and close the Picker view with transition

class MainVC: UIViewController {
var datasourceForUsers :UserPicker!
var userPicker :UIPickerView!
var toolBar: UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
userPicker = UIPickerView()
toolBar = UIToolbar()
datasourceForUsers = UserPicker()
}
#IBAction func onMoreTapped(){
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
#IBAction func showRegisteredUsers(_ sender: Any) {
createUserPicker()
configureToolBar(toolBar)
}
func createUserPicker() {
userPicker.dataSource = datasourceForUsers
userPicker.delegate = datasourceForUsers
//Customizations
userPicker.backgroundColor = .black
userPicker.frame = CGRect.init(x: 0.0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 300)
self.view.addSubview(userPicker)
}
func configureToolBar(_ toolBar:UIToolbar ){
toolBar.sizeToFit()
toolBar.barTintColor = .black
toolBar.tintColor = .white
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(MainVC.dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.frame = CGRect.init(x: 0.0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 50)
self.view.addSubview(toolBar)
}
override func viewWillAppear(_ animated: Bool) {
userPicker.reloadAllComponents()
toolBar.reloadInputViews()
}
#objc func cancelDatePicker(){
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
self.resignFirstResponder()
}
}
I used above code to show some data on UIPickerView .It works fine.But When I change the datasource(Add new Data) where the data is stored it does not reflect on the UI picker.In order to do that I have to run the application again.Second thing is I couldn't close the UI picker with transition.

Show back button without navigation view controller

I have the scheme: UITabBarViewController (with 3 tabs).
In all that tabs I don't want to show navigation menu on top.
And from the first tab, I want to push another view controller from button click that will have "back" button (and top toolbar with "cancel")
I tried some ways - in storyboard with push segue - no back button.
Probably because i don't have navigation view controller, so my navigation stack is empty.
Programmatically:
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "AddCoinTableViewController") as! AddCoinTableViewController
self.present(nextViewController, animated:true, completion:nil)
If I embed tabs in navigation controller, then I have top toolbar (which I don't want).
Any ideas how to make it?
You can't achieve navigation functionality without using UINavigationController. I mean you have to do all animation kind of stuff on your own, and I think that's not a good idea. Rather than that, you can use UINavigationController, and if you don't want to show navigationBar at some viewController, than do as follows for those view controllers.
override func viewWillApear() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = true
}
override func viewWillDisappear(animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
}
You can embed the navigation controller at your first tab controller (or any you want), and hide it at the controllers you don't want on their viewDidLoad like this:
self.navigationController?.isNavigationBarHidden = true
Doing this, you will be able to see the back Button at the controllers you pushed and didn't hide the navigationBar.
Make sure you push the controller using the navigation controller like this:
self.navigationController?.pushViewController(YOUR VIEW CONTROLLER, animated: true)
The below code will allow you to create your own Navigation handling class and have the "push" "pop" animation that UINavigationController has.. You can create a new project, copy paste the below into ViewController.swift and see for yourself..
Now you can give any UIViewController navigation controller abilities..
import UIKit
class NavigationHandler : NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning, UINavigationBarDelegate {
private var isPresenting: Bool = false
private weak var controller: UIViewController?
init(controller: UIViewController) {
super.init()
self.controller = controller
controller.transitioningDelegate = self
let navigationBar = UINavigationBar()
controller.view.addSubview(navigationBar)
NSLayoutConstraint.activate([
navigationBar.leftAnchor.constraint(equalTo: controller.view.leftAnchor),
navigationBar.rightAnchor.constraint(equalTo: controller.view.rightAnchor),
navigationBar.topAnchor.constraint(equalTo: controller.view.safeAreaLayoutGuide.topAnchor)
])
navigationBar.translatesAutoresizingMaskIntoConstraints = false
navigationBar.delegate = self
let item = UINavigationItem(title: controller.title ?? "")
let barButton = UIBarButtonItem(title: "Back", style: .done, target: self, action: #selector(onBackButton(button:)))
item.leftBarButtonItems = [barButton]
navigationBar.setItems([item], animated: true)
}
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
#objc
private func onBackButton(button: UIBarButtonItem) {
self.controller?.dismiss(animated: true, completion: nil)
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = true;
return self;
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = false;
return self;
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.25;
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let duration = self.transitionDuration(using: transitionContext)
let fromController = transitionContext.viewController(forKey: .from)
let toController = transitionContext.viewController(forKey: .to)
let containerView = transitionContext.containerView
if self.isPresenting {
let frame = fromController!.view.frame
containerView.addSubview(toController!.view)
toController?.view.frame = CGRect(x: frame.origin.x + frame.width, y: frame.origin.y, width: frame.width, height: frame.height)
UIView.animate(withDuration: duration, animations: {
fromController?.view.frame = CGRect(x: frame.origin.x - frame.size.width, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
toController?.view.frame = frame
}, completion: { (completed) in
transitionContext.completeTransition(true)
})
}
else {
let frame = fromController!.view.frame
containerView.insertSubview(toController!.view, at: 0)
toController?.view.frame = CGRect(x: frame.origin.x - frame.size.width, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
UIView.animate(withDuration: duration, animations: {
fromController?.view.frame = CGRect(x: frame.origin.x + frame.width, y: frame.origin.y, width: frame.width, height: frame.height)
toController?.view.frame = frame
}, completion: { (completed) in
transitionContext.completeTransition(true)
})
}
}
}
View Controllers for testing:
class ViewController : UIViewController {
private var navigationHandler: NavigationHandler?
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .custom)
button.setTitle("Push Controller", for: .normal)
button.setTitleColor(UIColor.red, for: .normal)
button.layer.borderColor = UIColor.black.cgColor
button.layer.borderWidth = 1.0
button.layer.cornerRadius = 5.0
button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
button.heightAnchor.constraint(equalToConstant: 45.0),
button.widthAnchor.constraint(equalToConstant: 150.0)
])
button.addTarget(self, action: #selector(onPush(button:)), for: .touchUpInside)
}
#objc
private func onPush(button: UIButton) {
let child = ChildViewController()
self.navigationHandler = NavigationHandler(controller: child)
self.present(child, animated: true, completion: nil)
}
}
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue
}
}

how do I make this pop up extension more generic to be applied to any view?

I have edited a pop up extension I found on github to the following
PopUpExtension.swift :
import UIKit
extension (Home)
{
//Call this method in view did load
func PopUpInit()
{
createOverlay()
createAlert()
}
func createOverlay()
{
let p = CGRectMake(self.view!.frame.origin.x, self.view!.frame.origin.y, self.view!.bounds.size.width, self.view!.bounds.size.height)
overlayView = UIView(frame: p)
overlayView.backgroundColor = UIColor.grayColor()
overlayView.alpha = 0.0
self.view!.addSubview(overlayView)
}
func createAlert()
{
let alertWidth: CGFloat = UIScreen.mainScreen().bounds.width
let alertHeight: CGFloat = UIScreen.mainScreen().bounds.height
let alertViewFrame: CGRect = CGRectMake(0,0, alertWidth, alertHeight)
alertView = UIView(frame: alertViewFrame)
alertView.center = self.view.center
alertView.backgroundColor = UIColor.clearColor()
alertView.alpha = 0.0
let button = UIButton(type: UIButtonType.System)
button.setTitle("Cancel", forState: UIControlState.Normal)
button.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
button.backgroundColor = UIColor.clearColor()
button.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
button.addTarget(self, action: "dismissAlert", forControlEvents: UIControlEvents.TouchUpInside)
button.center.x = alertView.center.x
let button1 = UIButton(type: UIButtonType.RoundedRect)
button1.setTitle("Send", forState: UIControlState.Normal)
button1.backgroundColor = UIColor.greenColor()
button1.frame = CGRectMake(100, 100, 80.0, 80.0)
button1.addTarget(self, action: "Button1", forControlEvents: UIControlEvents.TouchUpInside)
button1.center.x = alertView.center.x
let button2 = UIButton(type: UIButtonType.RoundedRect)
button2.setTitle("Send", forState: UIControlState.Normal)
button2.backgroundColor = UIColor.greenColor()
button2.frame = CGRectMake(100, 200, 80.0, 80.0)
button2.addTarget(self, action: "Button2", forControlEvents: UIControlEvents.TouchUpInside)
button2.center.x = alertView.center.x
let button3 = UIButton(type: UIButtonType.RoundedRect)
button3.setTitle("Send", forState: UIControlState.Normal)
button3.backgroundColor = UIColor.greenColor()
button3.frame = CGRectMake(100, 300, 80.0, 80.0)
button3.addTarget(self, action: "Button3", forControlEvents: UIControlEvents.TouchUpInside)
button3.center.x = alertView.center.x
alertView.addSubview(button)
alertView.addSubview(button1)
alertView.addSubview(button2)
alertView.addSubview(button3)
self.view!.addSubview(alertView)
}
func showAlert()
{
if (alertView == nil)
{
createAlert()
}
UIView.animateWithDuration(0.3)
{
self.overlayView.alpha = 0.5
self.alertView.alpha = 1.0
}
}
func dismissAlert()
{
UIView.animateWithDuration(0.15, animations:
{
self.overlayView.alpha = 0.0
self.alertView.alpha = 0.0
}, completion:
{
(value: Bool) in
self.alertView.removeFromSuperview()
self.alertView = nil
})
}
func Button1()
{
print("button1")
}
func Button2()
{
print("button2")
}
func Button3()
{
print("button3")
}
}
now in Home.swift view controller, I just do the following
import UIKit
class Home: UIViewController
{
override func viewDidLoad()
{
self.PopUpInit()
super.viewDidLoad()
}
var overlayView: UIView!
var alertView: UIView!
var animator: UIDynamicAnimator!
}
the question is :
how do I convert this extension into a class that I can use where ever I need, not only from Home.swift, also I want to avoid the need to define these into all classes that use this pop up
var overlayView: UIView!
var alertView: UIView!
var animator: UIDynamicAnimator!
can you guide me to the best way to do this?
thanks.
The trick to making this more generic is to make it more protocol-oriented. With protocols you can leverage default implementations where you define what the conforming type will need to be like and use the extension to implement methods.
Stuff:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
PopUpAble:
This will be your main protocol. Anything that has a view variable can be made PopUpAble.
#objc protocol PopUpAble : class {
var view : UIView! { get set }
func button1()
func button2()
func button3()
func alertWillDismiss() -> Bool
func alertDidDismiss()
func dismissAlert()
}
Types for identification:
These are protocols and conforming classes to be able to distinguish the alertView and overlayView without needing to assign them to attributes. Less boilerplate code -> Better
protocol AlertType : class {}
protocol OverlayType : class {}
class AlertView : UIView, AlertType {}
class OverlayView : UIView, OverlayType {}
The Implementation:
This is the same collection of functions as you had, only modified to use what is available in the PopUpAble protocol.
create... functions now search for existing matching types or create
no more setup function, was not needed
optional animation
set order of views before/after show/hide
This all makes it more versatile and less needs to implemented in the VC.
extension PopUpAble {
func createOverlay() -> UIView {
if let overlay = (view.subviews.filter { $0 is OverlayType }).first {
return overlay
}
let p = CGRectMake(self.view!.frame.origin.x, self.view!.frame.origin.y, self.view!.bounds.size.width, self.view!.bounds.size.height)
let overlayView = OverlayView(frame: p)
overlayView.backgroundColor = UIColor.grayColor()
overlayView.alpha = 0.0
view!.insertSubview(overlayView, atIndex: 0)
return overlayView
}
func createAlert() -> UIView {
if let alert = (view.subviews.filter { $0 is AlertType }).first {
return alert
}
let alertWidth: CGFloat = UIScreen.mainScreen().bounds.width
let alertHeight: CGFloat = UIScreen.mainScreen().bounds.height
let alertViewFrame: CGRect = CGRectMake(0,0, alertWidth, alertHeight)
let alertView = AlertView(frame: alertViewFrame)
alertView.center = self.view.center
alertView.backgroundColor = UIColor.clearColor()
alertView.alpha = 0.0
let button = UIButton(type: UIButtonType.System)
button.setTitle("Cancel", forState: UIControlState.Normal)
button.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
button.backgroundColor = UIColor.clearColor()
button.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
button.addTarget(self, action: #selector(Self.dismissAlert), forControlEvents: UIControlEvents.TouchUpInside)
button.center.x = alertView.center.x
let button1 = UIButton(type: UIButtonType.RoundedRect)
button1.setTitle("Send", forState: UIControlState.Normal)
button1.backgroundColor = UIColor.greenColor()
button1.frame = CGRectMake(100, 100, 80.0, 80.0)
button1.addTarget(self, action: #selector(Self.button1), forControlEvents: UIControlEvents.TouchUpInside)
button1.center.x = alertView.center.x
let button2 = UIButton(type: UIButtonType.RoundedRect)
button2.setTitle("Send", forState: UIControlState.Normal)
button2.backgroundColor = UIColor.greenColor()
button2.frame = CGRectMake(100, 200, 80.0, 80.0)
button2.addTarget(self, action: #selector(Self.button2), forControlEvents: UIControlEvents.TouchUpInside)
button2.center.x = alertView.center.x
let button3 = UIButton(type: UIButtonType.RoundedRect)
button3.setTitle("Send", forState: UIControlState.Normal)
button3.backgroundColor = UIColor.greenColor()
button3.frame = CGRectMake(100, 300, 80.0, 80.0)
button3.addTarget(self, action: #selector(Self.button3), forControlEvents: UIControlEvents.TouchUpInside)
button3.center.x = alertView.center.x
alertView.addSubview(button)
alertView.addSubview(button1)
alertView.addSubview(button2)
alertView.addSubview(button3)
view!.insertSubview(alertView, atIndex: 1)
return alertView
}
func showAlert(animated animated:Bool = true) {
func show(alert:UIView,overlay:UIView) {
if animated {
UIView.animateWithDuration(0.3) {
overlay.alpha = 0.5
alert.alpha = 1.0
}
} else {
overlay.alpha = 0.5
alert.alpha = 1.0
}
}
let overlay = createOverlay()
let alert = createAlert()
view.bringSubviewToFront(overlay)
view.bringSubviewToFront(alert)
show(alert, overlay: overlay)
}
func hideAlert(animated animated:Bool = true) {
guard alertWillDismiss() else {
return
}
func hide(alert:UIView,overlay:UIView) {
if animated {
UIView.animateWithDuration(0.15, animations: {
overlay.alpha = 0.0
alert.alpha = 0.0
}) {
if $0 {
self.view.sendSubviewToBack(alert)
self.view.sendSubviewToBack(overlay)
self.alertDidDismiss()
}
}
} else {
overlay.alpha = 0.0
alert.alpha = 0.0
view.sendSubviewToBack(alert)
view.sendSubviewToBack(overlay)
alertDidDismiss()
}
}
let overlay = createOverlay()
let alert = createAlert()
hide(alert, overlay: overlay)
}
}
The Works:
alertWillDismiss and alertDidDismiss are not needed, but they follow nicely with the standard UIKit flow and give you more control.
class VC : UIViewController, PopUpAble {
func button1() {
print("button1")
}
func button2() {
print("button2")
}
func button3() {
print("button3")
}
func alertWillDismiss() -> Bool {
return true
}
func alertDidDismiss() {
print("dismissed")
}
func dismissAlert() { // this bridges between objc en swift
self.hideAlert()
}
}
let vc = VC()
vc.view.backgroundColor = UIColor.greenColor()
vc.showAlert(animated: true)
XCPlaygroundPage.currentPage.liveView = vc.view
Another way to go is to get the current visible viewcontroller and present the alert on it. Instead of defining actions in a UIViewController subclass they are implemented in a PopUp object. When needed a PopUp is created from wherever and it will be displayed on the current VC.
Stuff:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
Current ViewController:
Can't take the credit for this code, is from somewhere on SO. (if someone has a link?)
public extension UIWindow {
var visibleViewController: UIViewController? {
return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
}
private static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
if let nc = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
} else if let tc = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
} else {
if let pvc = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(pvc)
} else {
return vc
}
}
}
}
Same as the other answer:
protocol AlertType : class {}
protocol OverlayType : class {}
class AlertView : UIView, AlertType {}
class OverlayView : UIView, OverlayType {}
The PopUp Object:
Subclassing can be used to override the actions.
class PopUp : NSObject {
func button1Tapped() {
print("button1")
}
func button2Tapped() {
print("button2")
}
func button3Tapped() {
print("button3")
}
func alertWillDismiss() -> Bool {
return true
}
func alertDidDismiss() {
print("dismissed")
}
This is now implemented in the PopUp
The view of the current VC is grabbed and used to display the alert.
func createOverlay() -> UIView? {
guard let window = UIApplication.sharedApplication().windows.first, view = window.visibleViewController?.view else {
return nil
}
if let overlay = (view.subviews.filter { $0 is OverlayType }).first {
return overlay
}
let p = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.bounds.size.width, view.bounds.size.height)
let overlayView = OverlayView(frame: p)
overlayView.backgroundColor = UIColor.grayColor()
overlayView.alpha = 0.0
view.insertSubview(overlayView, atIndex: 0)
return overlayView
}
func createAlert() -> UIView? {
guard let window = UIApplication.sharedApplication().windows.first, view = window.visibleViewController?.view else {
return nil
}
if let alert = (view.subviews.filter { $0 is AlertType }).first {
return alert
}
let alertWidth: CGFloat = UIScreen.mainScreen().bounds.width
let alertHeight: CGFloat = UIScreen.mainScreen().bounds.height
let alertViewFrame: CGRect = CGRectMake(0,0, alertWidth, alertHeight)
let alertView = AlertView(frame: alertViewFrame)
alertView.center = view.center
alertView.backgroundColor = UIColor.clearColor()
alertView.alpha = 0.0
let button = UIButton(type: UIButtonType.System)
button.setTitle("Cancel", forState: UIControlState.Normal)
button.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
button.backgroundColor = UIColor.clearColor()
button.frame = CGRectMake(0, 0, view.frame.width, view.frame.height)
button.addTarget(self, action: #selector(dismissAlert), forControlEvents: UIControlEvents.TouchUpInside)
button.center.x = alertView.center.x
let button1 = UIButton(type: UIButtonType.RoundedRect)
button1.setTitle("Send", forState: UIControlState.Normal)
button1.backgroundColor = UIColor.greenColor()
button1.frame = CGRectMake(100, 100, 80.0, 80.0)
button1.addTarget(self, action: #selector(button1Tapped), forControlEvents: UIControlEvents.TouchUpInside)
button1.center.x = alertView.center.x
let button2 = UIButton(type: UIButtonType.RoundedRect)
button2.setTitle("Send", forState: UIControlState.Normal)
button2.backgroundColor = UIColor.greenColor()
button2.frame = CGRectMake(100, 200, 80.0, 80.0)
button2.addTarget(self, action: #selector(button2Tapped), forControlEvents: UIControlEvents.TouchUpInside)
button2.center.x = alertView.center.x
let button3 = UIButton(type: UIButtonType.RoundedRect)
button3.setTitle("Send", forState: UIControlState.Normal)
button3.backgroundColor = UIColor.greenColor()
button3.frame = CGRectMake(100, 300, 80.0, 80.0)
button3.addTarget(self, action: #selector(button3Tapped), forControlEvents: UIControlEvents.TouchUpInside)
button3.center.x = alertView.center.x
alertView.addSubview(button)
alertView.addSubview(button1)
alertView.addSubview(button2)
alertView.addSubview(button3)
view.insertSubview(alertView, atIndex: 1)
return alertView
}
func showAlert(animated animated:Bool = true) {
guard let window = UIApplication.sharedApplication().windows.first, view = window.visibleViewController?.view else {
return
}
guard let overlay = createOverlay(), alert = createAlert() else {
return
}
func show(alert:UIView,overlay:UIView) {
if animated {
UIView.animateWithDuration(0.3) {
overlay.alpha = 0.5
alert.alpha = 1.0
}
} else {
overlay.alpha = 0.5
alert.alpha = 1.0
}
}
view.bringSubviewToFront(overlay)
view.bringSubviewToFront(alert)
show(alert, overlay: overlay)
}
func dismissAlert(animated animated:Bool = true) {
guard let window = UIApplication.sharedApplication().windows.first, view = window.visibleViewController?.view else {
return
}
guard let overlay = createOverlay(), alert = createAlert() else {
return
}
func hide(alert:UIView,overlay:UIView) {
if animated {
UIView.animateWithDuration(0.15, animations: {
overlay.alpha = 0.0
alert.alpha = 0.0
}) {
if $0 {
view.sendSubviewToBack(alert)
view.sendSubviewToBack(overlay)
self.alertDidDismiss()
}
}
} else {
overlay.alpha = 0.0
alert.alpha = 0.0
view.sendSubviewToBack(alert)
view.sendSubviewToBack(overlay)
alertDidDismiss()
}
}
hide(alert, overlay: overlay)
}
}
The Works:
let vc = UIViewController()
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.makeKeyAndVisible()
window.rootViewController = vc
vc.view.backgroundColor = UIColor.greenColor()
XCPlaygroundPage.currentPage.liveView = vc.view
let pop = PopUp()
pop.showAlert(animated: true)
Note:
I would remove the alert and overlay from the view after hiding. No need to keep them around.

Swift PresentViewController Dismisses Keyboard

I'm creating a Sign Up wizard with multiple UIViewControllers.
I currently have it setup so a user enter's his email, clicks the "Go" button on the keyboard and the next UIViewController slides in from the right where the user will enter his name. The problem though is that when I call presentViewController to bring the next UIViewController in, it dismisses the keyboard.
I'd like the keyboard to stay open the entire time while switching ViewControllers. If you look at Facebook's iOS app, they do what I'm trying to do with their signup page.
Any help or suggestions will be greatly appreciated. I read something about using an overlay window, but am not sure how to go about it since I will have multiple UIViewController's in my Sign Up wizard.
Here's my initial controller in the Sign Up Wizard:
class SignUpEmailViewController: UIViewController {
var titleLabel = UILabel.newAutoLayoutView()
var emailField = SignUpTextField(placeholder: "Enter your email address")
var emailLabel = UILabel.newAutoLayoutView()
var continueButton = SignUpContinueButton.newAutoLayoutView()
var footerView = SignUpFooterView.newAutoLayoutView()
let presentAnimationController = PushInFromLeftAnimationController()
let dismissAnimationController = PushInFromRightAnimationController()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupGestures()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
emailField.becomeFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupViews() {
view.backgroundColor = UIColor.colorFromCode(0xe9eaed)
titleLabel.text = "Get Started"
titleLabel.font = UIFont(name: "AvenirNextLTPro-Demi", size: 18)
titleLabel.textColor = Application.greenColor
emailField.enablesReturnKeyAutomatically = true
emailField.returnKeyType = .Go
emailField.delegate = self
emailLabel.text = "You'll use this email when you log in and if you ever need to reset your password."
emailLabel.font = UIFont(name: "AvenirNextLTPro-Regular", size: 13)
emailLabel.textColor = .colorFromCode(0x4e5665)
emailLabel.numberOfLines = 0
emailLabel.textAlignment = .Center
continueButton.addTarget(self, action: "continueButtonPressed", forControlEvents: .TouchUpInside)
continueButton.hidden = true
view.addSubview(titleLabel)
view.addSubview(emailField)
view.addSubview(emailLabel)
view.addSubview(continueButton)
view.addSubview(footerView)
setupConstraints()
}
func setupGestures() {
let gestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeHandler")
gestureRecognizer.direction = .Down
view.addGestureRecognizer(gestureRecognizer)
let tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tapGesture)
}
func setupConstraints() {
titleLabel.autoAlignAxisToSuperviewAxis(.Vertical)
titleLabel.autoPinEdgeToSuperviewEdge(.Top, withInset: screenSize.height * 0.2)
emailField.autoAlignAxisToSuperviewAxis(.Vertical)
emailField.autoPinEdge(.Top, toEdge: .Bottom, ofView: titleLabel, withOffset: 15)
emailField.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.85, height: 40))
emailLabel.autoAlignAxisToSuperviewAxis(.Vertical)
emailLabel.autoPinEdge(.Top, toEdge: .Bottom, ofView: emailField, withOffset: 10)
emailLabel.autoSetDimension(.Width, toSize: screenSize.width * 0.85)
continueButton.autoAlignAxisToSuperviewAxis(.Vertical)
continueButton.autoPinEdge(.Top, toEdge: .Bottom, ofView: emailField, withOffset: 10)
continueButton.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.85, height: 30))
footerView.autoSetDimension(.Height, toSize: 44)
footerView.autoPinEdgeToSuperviewEdge(.Bottom)
footerView.autoPinEdgeToSuperviewEdge(.Leading)
footerView.autoPinEdgeToSuperviewEdge(.Trailing)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func swipeHandler() {
dismissViewControllerAnimated(true, completion: nil)
}
func continueButtonPressed() {
presentNextViewController()
}
func dismissKeyboard() {
view.endEditing(true)
}
func presentNextViewController() {
let toViewController = SignUpNameViewController()
toViewController.transitioningDelegate = self
toViewController.firstNameField.becomeFirstResponder()
presentViewController(toViewController, animated: true, completion: nil)
}
}
extension SignUpEmailViewController: UIViewControllerTransitioningDelegate {
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return presentAnimationController
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return dismissAnimationController
}
}
extension SignUpEmailViewController: UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
presentNextViewController()
return true
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
continueButton.hidden = true
emailLabel.hidden = false
return true
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
continueButton.hidden = false
emailLabel.hidden = true
return true
}
}
And here's the controller that I'm trying to present:
class SignUpNameViewController: UIViewController, UIViewControllerTransitioningDelegate {
var titleLabel = UILabel.newAutoLayoutView()
var textFieldContainer = UIView.newAutoLayoutView()
var firstNameField = SignUpTextField(placeholder: "First name")
var lastNameField = SignUpTextField(placeholder: "Last name")
var continueButton = SignUpContinueButton.newAutoLayoutView()
var footerView = SignUpFooterView.newAutoLayoutView()
let presentAnimationController = PushInFromLeftAnimationController()
let dismissAnimationController = PushInFromRightAnimationController()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
firstNameField.becomeFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupGestures()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupViews() {
view.backgroundColor = UIColor.colorFromCode(0xe9eaed)
titleLabel.text = "What's your name?"
titleLabel.font = UIFont(name: "AvenirNextLTPro-Demi", size: 18)
titleLabel.textColor = Application.greenColor
firstNameField.returnKeyType = .Next
firstNameField.enablesReturnKeyAutomatically = true
lastNameField.returnKeyType = .Next
lastNameField.enablesReturnKeyAutomatically = true
continueButton.addTarget(self, action: "continueButtonPressed", forControlEvents: .TouchUpInside)
view.addSubview(titleLabel)
view.addSubview(textFieldContainer)
textFieldContainer.addSubview(firstNameField)
textFieldContainer.addSubview(lastNameField)
view.addSubview(continueButton)
view.addSubview(footerView)
setupConstraints()
}
func setupGestures() {
let gestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeHandler")
gestureRecognizer.direction = .Right
view.addGestureRecognizer(gestureRecognizer)
let tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tapGesture)
}
func setupConstraints() {
titleLabel.autoAlignAxisToSuperviewAxis(.Vertical)
titleLabel.autoPinEdgeToSuperviewEdge(.Top, withInset: screenSize.height * 0.2)
textFieldContainer.autoPinEdge(.Top, toEdge: .Bottom, ofView: titleLabel, withOffset: 15)
textFieldContainer.autoAlignAxisToSuperviewAxis(.Vertical)
textFieldContainer.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.8, height: 40))
let spaceBetweenTextFields: CGFloat = 5
let textFieldSize = ((screenSize.width * 0.8) - spaceBetweenTextFields) / 2
let textFields: NSArray = [firstNameField, lastNameField]
textFields.autoDistributeViewsAlongAxis(.Horizontal, alignedTo: .Horizontal, withFixedSize: textFieldSize, insetSpacing: false)
firstNameField.autoPinEdgeToSuperviewEdge(.Top)
firstNameField.autoPinEdgeToSuperviewEdge(.Bottom)
lastNameField.autoPinEdgeToSuperviewEdge(.Top)
lastNameField.autoPinEdgeToSuperviewEdge(.Bottom)
continueButton.autoAlignAxisToSuperviewAxis(.Vertical)
continueButton.autoPinEdge(.Top, toEdge: .Bottom, ofView: textFieldContainer, withOffset: 10)
continueButton.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.8, height: 30))
footerView.autoSetDimension(.Height, toSize: 44)
footerView.autoPinEdgeToSuperviewEdge(.Bottom)
footerView.autoPinEdgeToSuperviewEdge(.Leading)
footerView.autoPinEdgeToSuperviewEdge(.Trailing)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
func dismissKeyboard() {
view.endEditing(true)
}
func swipeHandler() {
dismissViewControllerAnimated(true, completion: nil)
}
func continueButtonPressed() {
let toViewController = SignUpPasswordViewController()
toViewController.transitioningDelegate = self
presentViewController(toViewController, animated: true, completion: {})
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return presentAnimationController
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return dismissAnimationController
}
}
And here is my custom transition:
class PushInFromLeftAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.35
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let finalFrameForVC = transitionContext.finalFrameForViewController(toViewController)
let containerView = transitionContext.containerView()
let bounds = UIScreen.mainScreen().bounds
toViewController.view.frame = CGRectOffset(finalFrameForVC, bounds.size.width, 0)
containerView!.addSubview(toViewController.view)
UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
fromViewController.view.frame = CGRectOffset(finalFrameForVC, -bounds.size.width, 0)
toViewController.view.frame = finalFrameForVC
}, completion: {
finished in
transitionContext.completeTransition(true)
})
}
}
I think the keyboard is dismissed because the corresponding UIResponder will resign at the same time the ViewController will disappear.
Have you tried setting the next UITextField (in the next ViewController) as the first responder then? thus the keyboard will be linked to a new UIResponder before the previous one would end editing...

Resources