I have a viewcontroller that is embedded in a navigation controller. I wanted a Swipe right to go right left and change viewcontroller from VC1 to VC2, which thanks to people on stackoverflow i managed to do. However now i want to go the traditional way left right (traditional way for push), when swiping left on VC2 that goes back to VC1.
This is my code in VC1 (to swipe right to go to VC2):
override func viewDidLoad() {
super.viewDidLoad()
//Swipe right gesture
let gesture = UISwipeGestureRecognizer(target: self, action: #selector(SelectedTopicViewController.performChange))
gesture.direction = .right
self.view.addGestureRecognizer(gesture)
}
#objc func performChange() {
self.performSegue(withIdentifier: "goTo", sender: nil)
}
This is my code in VC2 (for swiping left to go back to VC1):
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: false)
//swipe left gesture
let gesture = UISwipeGestureRecognizer(target: self, action: #selector(SendMessageTopicViewController.performChange))
gesture.direction = .left
self.view.addGestureRecognizer(gesture)
// Do any additional setup after loading the view.
}
#objc func performChange() {
//self.navigationController?.popViewController(animated: true)
//print("swipe detected")
self.performSegue(withIdentifier: "goBackToRight", sender: nil)
}
Also this is my custom segue code (for both left and right swipe):
import Foundation
import UIKit
import QuartzCore
class SegueFromLeft: UIStoryboardSegue {
override func perform() {
let src: UIViewController = self.source
let dst: UIViewController = self.destination
let transition: CATransition = CATransition()
let timeFunc : CAMediaTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.duration = 0.25
transition.timingFunction = timeFunc
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromLeft
src.navigationController!.view.layer.add(transition, forKey: kCATransition)
src.navigationController!.pushViewController(dst, animated: false)
}
}
class SegueFromRight: UIStoryboardSegue {
override func perform() {
let src = self.source
let dst = self.destination
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width*2, y: 0) //Double the X-Axis
UIView.animate(withDuration: 0.25, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
}) { (finished) in
src.present(dst, animated: false, completion: nil)
}
}
}
And this is my storyboard, note that i have one segue going to VC2, and one going back to VC1 (both custom segue using the different classes).
Even if i have a print("swiped") when user swipes on VC2 it dosent get printed in console, hence why i think its something wrong with the swipes...
Conclusion: Swipe does work on VC1 and segue to VC2 works, however swipe on VC2 does not work at all...
Related
My swift code is trying to inherit view1 from parent class one to child class two. view1 is recongizined but when the code is run and the segue is applied nothing changes on the screen. view1 should change colors from pink to cyan. Its not I don't understand why the change is not being applied.
import UIKit
class one : UIViewController {
var view1 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[view1].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
view1.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
view1.backgroundColor = .systemPink
view.backgroundColor = .orange
view1.addTarget(self, action: #selector(move), for: .touchDown)
}
#objc func move(){
let vc = two()
vc.modalPresentationStyle = .overCurrentContext // actually .fullScreen would be better
self.present(vc, animated: true)
}
}
class two: one {
override func viewDidLoad() {
view1.backgroundColor = .cyan
}
}
Your code is working fine; the presentation of Two is happening. But you don't see anything, because:
The backgroundColor of Two's view is nil, i.e. .clear, so you don't see the background.
In Two, you never put anything into the interface. You talk to the button view1 in viewDidLoad, but unlike One's viewDidLoad, you never put that button into the interface. So you don't see the button (because it isn't there).
A minimal "fix" would be to call super in Two's viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad() // *
view1.backgroundColor = .cyan
}
Is there any possibilities to perform segue from Right To Left Without push or Persenting ViewController. The Following Code Working Fine with animation but if I use this class my TabBar is hidden. If I remove the code inside the Perform() TabBar is show but the animation is stop
Using MDCBottomNavigationBar
class SegueFromRight: UIStoryboardSegue {
override func perform() {
let src = self.source
let dst = self.destination
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)
UIView.animate(withDuration: 0.25,delay: 0.0,options: UIView.AnimationOptions.curveEaseInOut,animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
},completion: { finished in
src.navigationController?.pushViewController(dst, animated: false)
}
)
}
}
is there any other way to perform segue with animation without hiding TabBar ?
Finally I found the answer. Just Removing the line Push ViewController
class SegueFromRight: UIStoryboardSegue {
override func perform() {
let src = self.source
let dst = self.destination
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)
UIView.animate(withDuration: 0.25,delay: 0.0,options: UIView.AnimationOptions.curveEaseInOut,animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
},completion: { finished in
//Remove Following line if you want to segue modally
//src.navigationController?.pushViewController(dst, animated: false)
}
)
}
}
Try this
private func setupAlwaysVisibleView() {
guard let tabBarController = self.tabBarController else { return }
tabBarController.view.insertSubview(alwaysVisibleView, belowSubview: tabBarController.tabBar)
}
I got this problem on iPad after update yesterday: if I load the second view controller, it appears smaller that the initial one (see photos below). Interesting, that is happening only since my update to the new iOS on iPad. There is no such problem on iPhone: the both VCs have there the same size.
Here is the code:
import UIKit
class StartVC: UIViewController, ButtonDelegate {
var width = CGFloat()
var height = CGFloat()
override func viewDidLoad() {
super.viewDidLoad()
width = view.frame.width
height = view.frame.height
setTestBtn()
view.backgroundColor = UIColor.purple
}
func setTestBtn(){
let a = Button(frame: CGRect(x: 0,y: 0, width: width*0.2, height: width*0.2))
a.center = CGPoint(x: width*0.5, y: height*0.5)
a.backgroundColor = .cyan
a.delegate = self
a.setLabel()
a.label.text = "Go to the MainVC"
view.addSubview(a)
}
func buttonClicked(sender: Button) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mainVC") as! MainVC
let transition = CATransition()
transition.duration = 2
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
view.window!.layer.add(transition, forKey: kCATransition)
vc.view.frame = view.bounds
self.present(vc, animated: true, completion: nil)
print("# ViewController func buttonClicked() OK!")
}
import UIKit
class MainVC: UIViewController, ButtonDelegate {
var width = CGFloat()
var height = CGFloat()
override func viewDidLoad() {
super.viewDidLoad()
width = view.frame.width
height = view.frame.height
setTestBtn()
view.backgroundColor = .red
}
func setTestBtn(){
let a = Button(frame: CGRect(x: 0,y: 0, width: width*0.2, height: width*0.2))
a.center = CGPoint(x: width*0.5, y: height*0.5)
a.backgroundColor = .orange
a.delegate = self
a.setLabel()
a.label.text = "Go to the StartVC"
view.addSubview(a)
}
func buttonClicked(sender: Button) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "startVC") as! StartVC
let transition = CATransition()
transition.duration = 2
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
view.window!.layer.add(transition, forKey: kCATransition)
vc.view.frame = view.bounds
self.present(vc, animated: true, completion: nil)
print("# ViewController func buttonClicked() OK!")
}
}
Since the release of iOS 13, the modal presentation style of View Controllers is chosen automatically by the system by default.
If you wish to counter this effect, this can help you.
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: true, completion: nil)
Hope this helps!
After launch screen dismisses itself logo and title of my app (they are in container) should go closer to the top of the screen. Between dismissing launch screen and viewDidAppear method there is a strange 'blink' of my container in the background. As you can see I am using snapkit but it should have nothing to do with the problem. Here is my code:
class WelcomeScreenViewController: UIViewController {
var welcomeScreenView: WelcomeScreenView {
return view as! WelcomeScreenView
}
override func loadView() {
let contentView = WelcomeScreenView(frame: .zero)
view = contentView
}
override func viewDidLoad() {
super.viewDidLoad()
self.welcomeScreenView.checkWeatherButton.transform = CGAffineTransform(translationX: 0, y: 200)
self.welcomeScreenView.checkWeatherButton.addTarget(self, action: #selector(showCityChoiceVC), for: .touchUpInside)
navigationController?.isNavigationBarHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.welcomeScreenView.appNameLogoContainerVerticalConstraint?.isActive = false
self.welcomeScreenView.appNameLogoContainer.snp.makeConstraints({ (make) in
make.top.equalTo(self.welcomeScreenView).offset(100)
})
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: {
self.welcomeScreenView.layoutIfNeeded()
self.welcomeScreenView.checkWeatherButton.transform = CGAffineTransform(translationX: 0, y: 0)
}, completion: nil)
}
#objc private func showCityChoiceVC() {
self.navigationController?.pushViewController(RegisterViewController(), animated: true)
}
Blinking comes from setting constraints in viewDidAppear. Use viewWillAppear or viewDidLoad instead. viewDidAppear is invoked when your view actually appears on screen. So any changes that happen will be visible to the user.
My app currently has 3 VCs. The rootVC modally presents the second VC via UIButton--which works fine--but I'm having trouble getting the third VC appear after a UILabel tap in the second VC.
This is the code in the SecondVC that handles the tap:
var goToStats : UILabel {
var label = UILabel()
label.frame = CGRect(x:0, y:0, width: 300, height: 60)
label.center = CGPoint(x: view.center.x, y: view.center.y + 250)
label.text = "Statistical Breakdown"
label.font = UIFont(name: "Arial", size: 30)
label.textAlignment = .center
label.backgroundColor = UIColor(
displayP3Red: 1/255,
green: 102.0/255,
blue: 102.0/255,
alpha: 1)
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
return label
}
view.addSubview(goToStats)
}
#objc func handleTap() {
print("Tapped!")
let thirdVC = ThirdVC()
self.navigationController!.present(thirdVC, animated: true, completion: nil)
}
Upon running, I get the following error:
error
Is there any explanation? I thought maybe there is no navigation Controller associated with the second VC (since it's returning nil), but the VC itself sits on a navigational stack, so I don't think that's the case here. Is it a problem with my third VC? Here is the current code:
import Foundation
import UIKit
class thirdVC : ViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
}
}
Thanks for your time!
Your rootViewController could be a navigationController but when you present something like this,
let secondVC = SecondVC()
self.navigationController!.present(secondVC, animated: true, completion: nil)
This doesn't mean that SecondVC will have a navigationController. To have a navigationController for SecondVC, you need to embed this ViewController inside a navigationController as below,
let secondVC = SecondVC()
let secondNavC = UINavigationController(rootViewController: secondVC)
self.navigationController!.present(secondNavC, animated: true, completion: nil)
Now, if you will present ThirdVC as below from SecondVC then it will work
let thirdVC = ThirdVC()
self.navigationController!.present(thirdVC, animated: true, completion: nil)
If you haven't embedded second ViewController inside a UINavigationController, then you will get that crash because you are force unwrapping(!) the navigationController which is not available in second ViewController.