I have first ViewController and second ViewController. When I return to first ViewController from second ViewController after that I want to show buttons in first ViewController.
To returning I use this code:
self.dismiss(animated: true, completion: nil)
And this code to show buttons in first ViewController
#IBOutlet weak var infoButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var infoButtonTopConstraint: NSLayoutConstraint!
#IBOutlet weak var nextButtonBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var infoButtonHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var nextButtonHeightConstraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
showButtonAnimation()
//animaton()
}
func showButtonAnimation() {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.view.layoutIfNeeded()
})
}
But buttons not showing when I return from second ViewController.
Replace this
func showButtonAnimation() {
self.view.layoutIfNeeded()
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
self.view.layoutIfNeeded()
})
}
EDIT 1
viewWillAppear not getting called reason : you are presenting 2nd viewController as overCurrentContext modalPresentationStyle.
Just replace overCurrentContext with fullScreen and it works.
Whenever you call viewWillAppear keep in mind that view where not loaded in main view so animation will not work proper. You should use a viewDidAppear.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showButtonAnimation()
}
And in your animation method update as below because you change constraint value inside animation block that in need to out of it.
func showButtonAnimation() {
self.infoButtonTopConstraint.constant += self.infoButtonHeightConstraint.constant
self.nextButtonBottomConstraint.constant -= self.nextButtonHeightConstraint.constant
UIView.animate(withDuration: 1.0, delay: 0.0,
options: [], animations: {
self.view.layoutIfNeeded()
})
}
I have tried your code and it is working for the first time, but the problem might be every time the constant value of the button constraint are increased.
so they might be hiding behind your main view.
Solution:- you can take flag and change its value when animation is completed.
Now when you come back from the second controller then you have to check in the viewWillAppear that if the buttons are visible or not, if they are visible then you do not have to call the showButtonAnimation method.
You can see where the buttons are in current screen with clicking on
button to see the UI hierarchy.
Hope this will solve your issue.
You can add a delegate in your second viewController. like:
protocol SecondViewControllerDelegate {
func vcDismissed()
}
Class SecondViewController : UIViewController {
weak var delegate: SecondViewControllerDelegate
.....
//Where you are dismissing your viewController, call the delegate method in completion.
func someMethod() {
self.dismiss(animated: true, completion: {
self.delegate.vcDismissed()
})
}
}
Now in your FirstViewController, conform the protocol and implement the method like below:
extension FirstViewController: SecondViewControllerDelegate {
func vcDismissed() {
//Here call your showButton method
self.showButton()
}
}
And do not forget to set the delegate when your are creating the instance of your secondViewController where you are presenting it. like
objSecondViewController.delegate = self
Hope this helps. for any query feel free to leave a comment.
Related
A function whose argument is used for Extension of another class is described.
I want to change the value of the UIView property (alpha) that exists in that function "at the button tap in another class".
However, this function can be changed at ViewDidLoad time.
How can I change the value when tapping buttons?
If possible please write the code.
ViewController Class
class ViewController: UIViewController {
#IBOutlet weak var vHome: UICollectionView!
#IBOutlet weak var vSearch: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
selectRd(rd: 3) //This time can change calue of alpha.
}
}
extension ViewController {
func selectRd(rd reader:Int){
var animateView:UIView!
let animateVHome = vHome
let animateVSearch = vSearch
UIView.animate(withDuration: 0.3, delay: 0.0, animations: {
animateVHome.alpha = 0.0
animateVSearch.alpha = 0.0
}, completion: nil)
switch reader {
case 2:
animateView = animateVHome
case 3:
animateView = animateVSearch
default:
break
}
UIView.animate(withDuration: 0.3, delay: 0.3, animations: {
animateView.alpha = 1.0
}, completion: nil)
}
}
Another Class
class Another{
#objc func buttonTapped(sender : HeaderButton) {
let vC = ViewController
vC.selectRd(rd: 2) //This time can not!!
}
}
Please comment if you can not understand the question.
The problem is that you can't access a function of a class if its currently active , as this will result in a crash , so Add a var in viewControler
class ViewController: UIViewController {
#IBOutlet weak var vHome: UICollectionView!
#IBOutlet weak var vSearch: UICollectionView!
var index:Int?
override func viewDidLoad() {
super.viewDidLoad()
selectRd(rd: self.index) //This time can change calue of alpha.
}
}
..
class Another{
#objc func buttonTapped(sender : HeaderButton) {
let vC = ViewController
vC.index = 2 //This time can not!!
}
}
I'm trying to animate 3 squares from the left to the right. The squares should reappear from the left while they are disappearing from the right. The idea is that the squares represent clouds, so the idea is clear.
This is what I currently got:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
}, completion: nil)
}
}
This is the result:
The squares move from the left to the right. This is good. The problem is that once the animation is done the squares just reappear from where they started. It isn't a smooth continuous movement where the clouds move from the left to the right and slowly reappear from the left again. How do you achieve this? Should there be a second containerView? Or remove the containerView and animate individual clouds?
The answer Milan Nosáľ solved the problem. A note though:
I placed containerView2 above containerView1 with the exact same
constraints (they overlap each other). Just make sure IB doesn't
include containerView2 as a subview of containerView1.
I guess the simplest approach for you know is to use two exactly the same containers. You can use exactly the same code, and put the containerView2 above containerView to make sure that cover one another.
And then simply use:
class ViewController: UIViewController {
// contains the squares
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var containerView2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// this will transform the containerView2 to the left to appear as if it was exactly to the left of the containerView
containerView2.transform = CGAffineTransform.identity.translatedBy(x: -self.view.bounds.width, y: 0)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startAnimating()
}
func startAnimating() {
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .repeat, animations: {
self.containerView.center.x += self.view.bounds.width
self.containerView2.center.x += self.view.bounds.width
}, completion: nil)
}
}
The containerView2 will represent the clouds coming back from the left.
You could try something like putting containerView.center.x -= 200 right before starting the animation. This way it first appears from the left side.
I have a segmentControl with 2 indexes. It is inside a parent view controller. The parent view controller has 2 containerViews that each have an embedded viewController.
parentVC
|
segmentedControll _vcZero
| /
2containerVCs---------
\_vcOne
Inside the viewWillAppear methods of vcZero and vcOne I have some print statements.
The problem is when the parentVC appears on scene and selectedSegmentIndex = 0 shows, it's print statement runs, but the print statement that is inside vcOne also runs too. Considering vcOne isn't on scene it's code shouldn't run.
I understand that when the parentVC loads both containerViews are loaded into memory. I tried these 2 SO answers but neither prevented vcOne's print statements from printing.
link
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var vcZeroContainer: UIView!
#IBOutlet weak var vcOneContainer: UIView!
let viewControllerIdentifiers = ["vcZeroContainer", "vcOneContainer"]
#IBAction func segmentChanged(sender: UISegmentedControl) {
var newController = storyboard?.instantiateViewControllerWithIdentifier(viewControllerIdentifiers[sender.selectedSegmentIndex]) as UIViewController
let oldController = childViewControllers.last as UIViewController
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
newController.view.frame = oldController.view.frame
transitionFromViewController(oldController, toViewController: newController, duration: 0.25, options: .TransitionCrossDissolve, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
}
and
link
#IBAction func segmentChanged(sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex
{
case 0:
vcOneContainer?.removeFromSuperview()
vcOneContainer = nil
case 1:
vcZeroContainer?.removeFromSuperview()
vcZeroContainer = nil
default:
break;
}
}
How to I remove vcOneContainer from memory when selectedSegmentIndex = 0 is showing?
I am doing a transition of an UIImageView's image and would also like to move the image view at the same time. The results so far is that the image transitions, but not while moving.
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.transition(with: imageView, duration: 2.0, options: .transitionCrossDissolve, animations: {
self.imageView.image = UIImage(named: "otherImage")
}, completion: nil)
imageViewTopConstraint.constant = 200
UIView.animate(withDuration: 2.0) {
self.view.layoutIfNeeded()
}
}
}
Any ideas on how to solve this?
You can put your imageView inside a container, then animate the constraints of the container while doing the transition on the imageView.
I would like to make a UI that have label, table view and one button click. When click on the button, we pop up a half screen view that have lots of buttons. I want user can still click on the rest of the screen also.
So i use the approach that suggest in the post
How To Present Half Screen Modal View?
Method 2: to animate a UIView which is of size half of the existing view.
Then you have to simply follow animation of the UIView.
Here as it is just a UIView that will be added as subview to existing view, you will be able to touch the rest of the screen.
As i am newbie to the ios and swift, I would like to get some suggestions.
Now i am successfully add as subview and show in the half of the screen.
How can i implement to let subview click button result show on parent view label text?
I am thinking about parent.xib and subview.xib have the same UIVeiwController.swift. Then i can #IBOutlet and #IBAction to the same controller swift file and update the result. But don't know it is the accpetable way to do?
If not, how can the subViewController send result/event to the parent view and update in the parent view component?
You could use delegation. This keeps your view controllers decoupled, i.e. prevents the child from having a reference to its parent, which allows other view controllers to interact with the modal view controller in the same way.
class ParentViewController : UIViewController, ModalViewControllerDelegate {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
modalViewContorller.delegate = self
self.presentViewController( modalViewContorller, animated: true, completion: nil )
}
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String ) {
self.label.text = result
}
}
protocol ModalViewControllerDelegate {
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String )
}
class ModalViewController: UIViewController {
var delegate: ModalViewControllerDelegate?
#IBAction func buttonClicked( sender: AnyObject? ) {
delegate?.modalViewControllerDidProduceResult( self, result: "Hello!" )
}
}
You could also use a closure, which in Swift provides a more concise syntax.
class ParentViewController : UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
self.presentViewController( modalViewContorller, animated: true, completion: nil )
modalViewContorller.resultBlock = { (result: String) in
self.label.text = result
}
}
}
class ModalViewController: UIViewController {
var resultBlock: ((String) -> ())?
#IBAction func buttonClicked( sender: AnyObject? ) {
self.resultBlock?( "Hello!" )
}
}