Swift PresentViewController Dismisses Keyboard - ios

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...

Related

Interactive push view controller

I am looking to have an interactive push view controller. So if the user pans from the right edge of the screen, it will pop to the next view controller. I have found this CocoaPods: https://github.com/rickytan/RTInteractivePush, but it is written in Objective-C, so I am unsure how to use it. On my own I have been able to come up with a pan gesture that pushes a view, however it is not interactive:
swipeGesture = UIPanGestureRecognizer(target: self, action:#selector(swiped(_:)))
swipeGesture.delegate = self
view.addGestureRecognizer(swipeGesture)
#objc func swiped(_ gestureRecognizer: UIPanGestureRecognizer) {
let newView = View()
self.navigationController?.pushViewController(newView, animated: true)
}
Any help would be greatly appreciated!
You can do it programmatically with UIPageViewController:
Set your UIPageViewController class:
import UIKit
class MyControllerContainer: UIPageViewController {
// set UIPageViewController transition style
override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
print("init(coder:) has not been implemented")
}
var pages = [UIViewController]()
var pageControl = UIPageControl()
let initialPage = 0
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setup()
style()
layout()
}
}
Now set style, setup, and layout func:
extension MyControllerContainer {
func setup() {
dataSource = self
delegate = self
pageControl.addTarget(self, action: #selector(pageControlDragged(_:)), for: .valueChanged)
// create an array of viewController
let page1 = ViewController1()
let page2 = ViewController2()
let page3 = ViewController3()
pages.append(page1)
pages.append(page2)
pages.append(page3)
// set initial viewController to be displayed
setViewControllers([pages[initialPage]], direction: .forward, animated: true, completion: nil)
}
func style() {
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = .white
pageControl.pageIndicatorTintColor = UIColor(white: 1, alpha: 0.3)
pageControl.numberOfPages = pages.count
pageControl.currentPage = initialPage
}
func layout() {
view.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.widthAnchor.constraint(equalTo: view.widthAnchor),
pageControl.heightAnchor.constraint(equalToConstant: 20),
view.bottomAnchor.constraint(equalToSystemSpacingBelow: pageControl.bottomAnchor, multiplier: 1),
])
}
}
set how we change controller when pageControl Dragged:
extension MyControllerContainer {
// How we change controller when pageControl Dragged.
#objc func pageControlDragged(_ sender: UIPageControl) {
setViewControllers([pages[sender.currentPage]], direction: .forward, animated: true, completion: nil)
}
}
after that set UIPageViewController delegate and datasource:
// MARK: - DataSources
extension MyControllerContainer: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
if currentIndex == 0 {
return nil // stop presenting controllers when swipe from left to right in ViewController1
} else {
return pages[currentIndex - 1] // go previous
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let currentIndex = pages.firstIndex(of: viewController) else { return nil }
if currentIndex == 2 {
print("Last index...")
}
if currentIndex < pages.count - 1 {
return pages[currentIndex + 1] // go next
} else {
return nil // stop presenting controllers when swipe from right to left in ViewController3
}
}
}
// MARK: - Delegates
extension MyControllerContainer: UIPageViewControllerDelegate {
// How we keep our pageControl in sync with viewControllers
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let viewControllers = pageViewController.viewControllers else { return }
guard let currentIndex = pages.firstIndex(of: viewControllers[0]) else { return }
pageControl.currentPage = currentIndex
}
}
Now add your viewControllers, in my case 3:
// MARK: - ViewControllers
class ViewController1: UIViewController {
let mylabel1: UILabel = {
let label = UILabel()
label.text = "Controller 1"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
view.addSubview(mylabel1)
mylabel1.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel1.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel1.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
class ViewController2: UIViewController {
let mylabel2: UILabel = {
let label = UILabel()
label.text = "Controller 2"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
view.addSubview(mylabel2)
mylabel2.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel2.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel2.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
class ViewController3: UIViewController {
let mylabel3: UILabel = {
let label = UILabel()
label.text = "Controller 3"
label.textAlignment = .center
label.textColor = .white
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
view.addSubview(mylabel3)
mylabel3.heightAnchor.constraint(equalToConstant: 50).isActive = true
mylabel3.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
mylabel3.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mylabel3.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
This is the result:
The current code is perfect it you have only one viewcontroller up next.
But if you have to 2 or more viewController up next then interactive push is unuseful technique. Vies versa for the interactive pop controller we just have to pop top view form the navigation stack which make sense. Please have a look the the image below which describe the scenario for both push and pop.

Slide Menu (Hamburger Menu) iOS Swift

I need create slide menu with transition from right to left, I have succeeded to create it but when I press icon of menu show me slide menu without animation!! and I can’t hide it with press outside of slide menu!!
How can I create animation with transition from right to left??
How can I set hide when press outside of slide menu??
SlideInMenu Class:
import UIKit
class SlideInMenu: NSObject, UIViewControllerAnimatedTransitioning {
var isPresenting = false
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toVC = transitionContext.viewController(forKey: .to),
let fromVC = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toVC.view.bounds.width * 0.8375
let finalHeight = toVC.view.bounds.height
if (isPresenting) {
dimmingView.backgroundColor = UIColor(red: 32/255, green: 70/255, blue: 86/255, alpha: 0.8)
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
containerView.addSubview(toVC.view)
toVC.view.frame = CGRect(x: -(finalWidth - toVC.view.bounds.width), y: 0, width: finalWidth, height: finalHeight)
}
let transform = {
self.dimmingView.alpha = 0.9
toVC.view.transform = CGAffineTransform(translationX: finalWidth - toVC.view.bounds.width, y: 0)
}
let identiy = {
self.dimmingView.alpha = 0.9
fromVC.view.transform = .identity
}
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform () : identiy()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
}
}
}
ViewController Class:
import UIKit
class ViewController: UIViewController {
let transition = SlideInMenu()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func tapBtnMenu(_ sender: Any) {
guard let menuVC = storyboard?.instantiateViewController(withIdentifier: "MenuViewController") else { return }
menuVC.modalPresentationStyle = .overCurrentContext
menuVC.transitioningDelegate = self
present(menuVC, animated: true)
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.isPresenting = true
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.isPresenting = false
return transition
}
}
Output:
You can copy paste the below code in see it a in live action.
Explanation:
ViewController:
A button is added to ViewController for the opening of Sidebar
It confirms to the delegate so that it can listen to when the sidebar gets opened and when it gets closed and whether the user has selected any option of not
Sidebar:
This does the heavy lifting of main work.
The balck transparent View is close button and the other part is NavigationViewController
In show method, the close button is set to 60 width and navigation vc view is aligned next to it using constraints. Note that the x position of navigation vc is set to the width of screenwidth.
With the help of UIView.animate, the X position is set to 0 thus it animates from right to left.
on click on any option or clicking on outside of nav vc i.e. close button, the closeSidebar method is called.
closeSidebar again has a UIViw.animate block which is setting X position to screenwidth again thus left to right animation. and when it gets completed it is removed from the view hierarchy.
The ViewController is confirming to SidebarDelegate in this delegate's sidebarDidClose method, you will get to know that side bar has been closed and if the user has selected any option or not.
ViewController:
import UIKit
class ViewController: UIViewController {
public let button: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Open Menu", for: .normal)
v.setTitleColor(.blue, for: .normal)
v.setTitleColor(UIColor.blue.withAlphaComponent(0.5), for: .highlighted)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
let constrains = [
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
]
NSLayoutConstraint.activate(constrains)
button.addTarget(self, action: #selector(didSelect(_:)), for: .touchUpInside)
}
#objc func didSelect(_ sender: UIButton){
SidebarLauncher.init(delegate: self).show()
}
}
extension ViewController: SidebarDelegate{
func sidbarDidOpen() {
}
func sidebarDidClose(with item: NavigationModel?) {
guard let item = item else {return}
switch item.type {
case .home:
print("called home")
case .star:
print("called star")
case .about:
print("called about")
case .facebook:
print("called facebook")
case .instagram:
print("instagram")
}
}
}
SideBar:
import UIKit
protocol SidebarDelegate {
func sidbarDidOpen()
func sidebarDidClose(with item: NavigationModel?)
}
class SidebarLauncher: NSObject{
var view: UIView?
var delegate: SidebarDelegate?
var vc: NavigationViewController?
init(delegate: SidebarDelegate) {
super.init()
self.delegate = delegate
}
public let closeButton: UIButton = {
let v = UIButton()
v.backgroundColor = UIColor.black.withAlphaComponent(0.5)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
func show(){
let bounds = UIScreen.main.bounds
let v = UIView(frame: CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height))
v.backgroundColor = .clear
let vc = NavigationViewController()
vc.delegate = self
v.addSubview(vc.view)
v.addSubview(closeButton)
vc.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
closeButton.topAnchor.constraint(equalTo: v.topAnchor),
closeButton.leadingAnchor.constraint(equalTo: v.leadingAnchor),
closeButton.bottomAnchor.constraint(equalTo: v.bottomAnchor),
closeButton.widthAnchor.constraint(equalToConstant: 60),
vc.view.topAnchor.constraint(equalTo: v.topAnchor),
vc.view.leadingAnchor.constraint(equalTo: closeButton.trailingAnchor),
vc.view.bottomAnchor.constraint(equalTo: v.bottomAnchor),
vc.view.trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: 0)
])
closeButton.addTarget(self, action: #selector(close(_:)), for: .touchUpInside)
self.view = v
self.vc = vc
UIApplication.shared.keyWindow?.addSubview(v)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
self.view?.frame = CGRect(x: 0, y: 0, width: self.view!.frame.width, height: self.view!.frame.height)
self.view?.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}, completion: {completed in
self.delegate?.sidbarDidOpen()
})
}
#objc func close(_ sender: UIButton){
closeSidebar(option: nil)
}
func closeSidebar(option: NavigationModel?){
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
if let view = self.view{
view.frame = CGRect(x: view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
self.view?.backgroundColor = .clear
}
}, completion: {completed in
self.view?.removeFromSuperview()
self.view = nil
self.vc = nil
self.delegate?.sidebarDidClose(with: option)
})
}
}
extension SidebarLauncher: NavigationDelegate{
func navigation(didSelect: NavigationModel?) {
closeSidebar(option: didSelect)
}
}
For the completeness of the code
NavigationView
import Foundation
import UIKit
class NavigationView: UIView{
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
addSubview(mainView)
mainView.addSubview(collectionView)
setConstraints()
}
func setConstraints() {
let constraints = [
mainView.topAnchor.constraint(equalTo: topAnchor),
mainView.leadingAnchor.constraint(equalTo: leadingAnchor),
mainView.bottomAnchor.constraint(equalTo: bottomAnchor),
mainView.trailingAnchor.constraint(equalTo: trailingAnchor),
collectionView.topAnchor.constraint(equalTo: mainView.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor),
collectionView.bottomAnchor.constraint(equalTo: mainView.bottomAnchor),
collectionView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor)
]
NSLayoutConstraint.activate(constraints)
}
public let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
let v = UICollectionView(frame: .zero, collectionViewLayout: layout)
v.translatesAutoresizingMaskIntoConstraints = false
v.register(NavigationCell.self, forCellWithReuseIdentifier: "NavigationCell")
v.backgroundColor = .clear
return v
}()
public let mainView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .blue
return v
}()
}
NavigationViewController plus Navigation Model
import Foundation
import UIKit
protocol NavigationDelegate{
func navigation(didSelect: NavigationModel?)
}
enum NavigationTypes{
case home,star,about,facebook,instagram
}
struct NavigationModel {
let name: String
let type: NavigationTypes
}
class NavigationViewController: UIViewController{
var myView: NavigationView {return view as! NavigationView}
unowned var collectionView: UICollectionView {return myView.collectionView}
var delegate: NavigationDelegate?
var list = [NavigationModel]()
override func loadView() {
view = NavigationView()
}
override func viewDidLoad() {
super.viewDidLoad()
setupList()
collectionView.delegate = self
collectionView.dataSource = self
}
func setupList(){
list.append(NavigationModel(name: "Home", type: .home))
list.append(NavigationModel(name: "Star", type: .star))
list.append(NavigationModel(name: "About", type: .about))
list.append(NavigationModel(name: "Facebook", type: .facebook))
list.append(NavigationModel(name: "Instagram", type: .instagram))
}
}
extension NavigationViewController: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "NavigationCell", for: indexPath) as! NavigationCell
let model = list[indexPath.item]
cell.label.text = model.name
return cell
}
}
extension NavigationViewController: UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.navigation(didSelect: list[indexPath.item])
}
}
extension NavigationViewController: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 45)
}
}
NavigationCell
import Foundation
import UIKit
class NavigationCell: UICollectionViewCell{
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
[label,divider].forEach{contentView.addSubview($0)}
setConstraints()
}
func setConstraints() {
let constraints = [
label.centerYAnchor.constraint(equalTo: centerYAnchor),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
divider.leadingAnchor.constraint(equalTo: leadingAnchor),
divider.bottomAnchor.constraint(equalTo: bottomAnchor),
divider.trailingAnchor.constraint(equalTo: trailingAnchor),
divider.heightAnchor.constraint(equalToConstant: 1)
]
NSLayoutConstraint.activate(constraints)
}
public let label: UILabel = {
let v = UILabel()
v.text = "Label Text"
v.textColor = .white
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
public let divider: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
}

my question is about tap gesture.that is not working

class menuView
{
let View = UIView()
let resignView = UIView()
let tap = UITapGestureRecognizer()
func makeView(view:UIView){
makeResignView(view: view)
view.addSubview(View)
View.translatesAutoresizingMaskIntoConstraints = false
View.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
View.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
View.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
View.widthAnchor.constraint(equalToConstant: view.frame.width - 100).isActive = true
View.backgroundColor = UIColor.cyan
}
func makeResignView(view:UIView){
print("resing view is activate")
resignView.frame = view.frame
view.addSubview(resignView)
resignView.backgroundColor = UIColor.blue
resignView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(handleDismiss(recog:)))
resignView.addGestureRecognizer(tap)
}
#objc func handleDismiss(recog:UITapGestureRecognizer){
print("rsing view is dismiss")
View.removeFromSuperview()
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.gray
}
#IBAction func PlaceView(_ sender: Any) {
let NewView = menuView()
NewView.resignView.frame = view.frame
NewView.makeResignView(view: self.view)
NewView.makeView(view: self.view)
}
}
gesture is not working.
In the menuView class i make a view and add a gesture to it .In the viewController class i add the menuView and run the code.the view is added but the gesture is not working.
The correct way should have been to inherit subview with UIView class.
See below example -
override func viewDidLoad() {
super.viewDidLoad()
let newView = subView()
newView.addGuesture()
self.view.addSubview(newView)
// Do any additional setup after loading the view, typically from a nib.
}
class subView:UIView{
func addGuesture(){
let tap = UITapGestureRecognizer()
tap.addTarget(self,action:#selector(handleTap))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tap)
self.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
self.backgroundColor = UIColor.red;
}
#objc func handleTap(){
print("tap is working")
}
}

Failed that ImageView in scrollView zoom in by swift3

Sorry I am new to swift and I have an imageView.
I can see my image in imageView.
I want to be able to zoom the image in and out. But I also fail to change my image size. What's wrong with my code? Sorry,I already find many solution to fix my problem. I don't know how to fix it. Should I add some gestureRecognizer or anything else?
Thank for answer.
This is code:
import UIKit
import SnapKit
class ImageViewController: UIViewController, UIScrollViewDelegate {
var url:String = ""
var id:String = ""
var scrollView:UIScrollView = { () -> UIScrollView in
let ui = UIScrollView()
ui.backgroundColor = UIColor.black
ui.minimumZoomScale = 1.0
ui.maximumZoomScale = 6.0
return ui
}()
var toolbar:UIToolbar = { () -> UIToolbar in
let ui = UIToolbar()
ui.barTintColor = defaultNavBackgroundColor
ui.clipsToBounds = true
ui.isTranslucent = false
ui.isUserInteractionEnabled = true
return ui
}()
var photoImageView:UIImageView = { () -> UIImageView in
let ui = UIImageView()
ui.backgroundColor = UIColor.clear
ui.layer.masksToBounds = true
ui.contentMode = .scaleAspectFit
ui.isUserInteractionEnabled = true
return ui
}()
var buttonDownload:UIBarButtonItem = { () -> UIBarButtonItem in
let ui = UIBarButtonItem()
ui.style = .plain
return ui
}()
override func loadView() {
super.loadView()
view.backgroundColor = UIColor.black
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// navigationItem.title = name
self.automaticallyAdjustsScrollViewInsets = false
imageFromUrl(imageView: photoImageView, url: url, chatroomId: id,isResize: false)
self.buttonDownload.action = #selector(btnDownloadClicked(sender:))
scrollView.delegate = self
scrollView.contentSize = (self.photoImageView.image!.size)
photoImageView.center = scrollView.center
loadContent()
loadVFL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadContent() {
toolbar.sizeToFit()
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
buttonDownload = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: nil)
toolbar.items = [flexSpace,buttonDownload]
view.addSubview(scrollView)
view.addSubview(toolbar)
scrollView.addSubview(photoImageView)
}
func loadVFL() {
scrollView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalTo(toolbar.snp.top)
make.left.equalToSuperview()
make.right.equalToSuperview()
}
photoImageView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalToSuperview()
make.left.equalToSuperview()
make.right.equalToSuperview()
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
}
toolbar.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(44)
}
}
func btnDownloadClicked(sender:UIBarButtonItem) {
print("press")
}
// MARK: - UIScrollViewDelegate
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return photoImageView
}
}
image like this

How to enable leftBarButtonItem from other ViewController?

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
}
}

Resources