Swift - Custom Segue Error (Ambiguous use of 'view') - ios

I am trying to make a custom segue and it keeps telling me that I am using an ambiguous use of 'view'. Can anyone explain what's going on?
Code:
import UIKit
class customSegue: UIStoryboardSegue {
override func perform() {
let sourceVC: AnyObject = self.sourceViewController
let destinationVC: AnyObject = self.destinationViewController
sourceVC.view.addSubview(destinationVC.view)
destinationVC.view.transform = CGAffineTransformMakeScale(0.05, 0.05)
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
destinationVC.view.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (finished) -> Void in
destinationVC.view.removeFromSuperview()
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
sourceVC.presentViewController(destinationVC, animated: false, completion: nil)
}
}
}
}
The 3 Lines of code that give error - Ambiguous use of 'view' :
sourceVC.view.addSubview(destinationVC.view)
destinationVC.view.transform = CGAffineTransformMakeScale(0.05, 0.05)
destinationVC.view.transform = CGAffineTransformMakeScale(1.0, 1.0)

The reason you are getting these errors is that you are typing your sourceVC and destinationVC variables as AnyObject. You must type your variables as a UIViewController so swift can see the correct view attribute. Your variable declaration would then look like this:
let sourceVC: UIViewController = self.sourceViewController
let destinationVC: UIViewController = self.destinationViewController

Related

Conditional cast from UIViewController always succeeds | Swift/Xcode

What am I doing wrong here? This still functions fine, but I would like to get rid of the yellow warning if I can help it. The warning is on the "if" statement. If I remove the "?" on "as", then the code won't even run... it requires it yet complains about it.
The warning:
Conditional cast from 'UIViewController' to 'UIViewController' always succeeds
The code:
class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
if let sourceViewController = self.source as? UIViewController, let destinationViewController = self.destination as? UIViewController {
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}
}
}
You don't need to cast it to UIViewController because properties source and destinations is UIViewController already
open var source: UIViewController { get }
open var destination: UIViewController { get }
You seeing this warning because you cast from not Optional UIViewController to optional UIViewController.
When you remove as? your code isn't running because you trying to unwrap not optional property.
Initializer for conditional binding must have Optional type, not 'UIViewController'
You should remove if do something like that:
final class FadeInPushSegue: UIStoryboardSegue {
var animated: Bool = true
override func perform() {
let sourceViewController = self.source
let destinationViewController = self.destination
let transition: CATransition = CATransition()
transition.type = CATransitionType.fade; sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
}

Xcode 9: Getting More Information On Memory Leak

After learning that a memory leak may be the cause of my iOS app crashing hours after being installed onto my phone, I've watched and read videos/articles surrounding memory leaks, specifically in Xcode and how to debug them. I've been trying to use the Memory Debugger and Xcode leaks instrument with no luck. I am wondering, how do I find the source of my leaks? The screenshot I attached shows one of the many leaks and it never lets me dig deeper than what the picture shows. In videos, Xcode takes them to the line of code causing the issue using the backtrace, however I have not been able to do that in any of the cases. I am also noticing the leaks are coming from the UIKIT, is this normal? Appreciate any help as I am fairly new to Xcode.
Here is the image.
Here is some code from my initial log in view controller. In the second image you'll see that the only view that has a memory leak is this one, however - I can't dig deeper on this either.
Second image of memory leak from InitialLogInVC
import UIKit
class IntialLoginInViewController: UIViewController {
#IBOutlet weak var backgroundAlbumArt: UIImageView!
#IBOutlet weak var foregroundAlbumArt: UIImageView!
#IBOutlet weak var musicNameLabel: UILabel!
private weak var imageOne = UIImage(named: "taste.jpg")
private weak var imageTwo = UIImage(named: "NavReckless.jpg")
private weak var imageThree = UIImage(named: "watch.jpg")
private weak var imageFour = UIImage(named: "juicewrld.jpg")
lazy var imageInformation = [(name:"Taste - Tyga", image:imageOne),(name:"Reckless - Nav", image: imageTwo),(name:"Watch - Travis Scott", image: imageThree), (name:"Goodbye & Good Riddance - Juice Wrld", image: imageFour)]
private var currentIndex = 0
static var spotifySession: AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
foregroundAlbumArt.image = imageOne
backgroundAlbumArt.image = imageOne
musicNameLabel.text = imageInformation[0].name
let _ = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(imageRefresh), userInfo: nil, repeats: true)
}
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
#objc func imageRefresh(){
if currentIndex == imageInformation.count - 1 {
currentIndex = 0
}
else {
currentIndex += 1
}
//Update Label
UIView.transition(with: self.musicNameLabel, duration: 2.0, options: [.transitionCrossDissolve] , animations: {
self.musicNameLabel.text = self.imageInformation[self.currentIndex].name
}, completion: nil)
//Update foreground Album
UIView.transition(with: self.foregroundAlbumArt, duration: 2.0, options: [.transitionCrossDissolve] , animations: {
self.foregroundAlbumArt.image = self.imageInformation[self.currentIndex].image
}, completion: nil)
//Update background Album
UIView.transition(with: self.backgroundAlbumArt, duration: 2.0, options: [.transitionCrossDissolve] , animations: {
self.backgroundAlbumArt.image = self.imageInformation[self.currentIndex].image
}, completion: nil)
}
}
try this
//Update Label
UIView.transition(with: self.musicNameLabel, duration: 2.0, options: [.transitionCrossDissolve] , animations: { [weak self] in
guard let `self` = self else { return }
self.musicNameLabel.text = self.imageInformation[self.currentIndex].name
}, completion: nil)
//Update foreground Album
UIView.transition(with: self.foregroundAlbumArt, duration: 2.0, options: [.transitionCrossDissolve] , animations: { [weak self] in
guard let `self` = self else { return }
self.foregroundAlbumArt.image = self.imageInformation[self.currentIndex].image
}, completion: nil)
//Update background Album
UIView.transition(with: self.backgroundAlbumArt, duration: 2.0, options: [.transitionCrossDissolve] , animations: { [weak self] in
guard let `self` = self else { return }
self.backgroundAlbumArt.image = self.imageInformation[self.currentIndex].image
}, completion: nil)

When is the destination view controller initialized during a segue?

I'm attempting to write a protocol and a custom UIStoryboardSegue class that will allow me to easily implement custom transitions in my UIViewControllers:
public protocol TransitionController
{
var transitionDurationIn: CFTimeInterval { get }
var transitionDurationOut: CFTimeInterval { get }
func prepareTransitionIn()
func prepareTransitionOut()
func performTransitionIn(finished: #escaping () -> Void)
func performTransitionOut(finished: #escaping () -> Void)
}
class JFTransitionControllerSegue: UIStoryboardSegue {
override func perform() {
let defaultTransitionDuration : CFTimeInterval = 1.5
if let dvc = self.destination as? TransitionController {
dvc.prepareTransitionIn()
}
else {
// Default transition
self.destination.view.alpha = 0
}
if let svc = self.source as? TransitionController {
svc.prepareTransitionOut()
svc.performTransitionOut(){ () in
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
else
{
// Default transition for the source controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.source.view.alpha = 0
}) { (Finished) in
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
}
}
class TestController: UIViewController, TransitionController {
#IBOutlet weak var form_username: UITextField!
// MARK: - TransitionController Protocol
var transitionDurationIn : CFTimeInterval {return 1.0}
var transitionDurationOut : CFTimeInterval {return 1.0}
func prepareTransitionIn()
{
//self.view.alpha = 0 // no fade in if you uncomment
form_username.alpha = 0 // nil
}
func prepareTransitionOut()
{
self.view.alpha = 1 // works
}
func performTransitionIn(finished: #escaping () -> Void)
{
UIView.animate(withDuration: self.transitionDurationIn, animations: {
//self.view.alpha = 1 // no fade in if you uncomment
self.form_username.alpha = 1 // nil, crashes
}) { (Finished) in
finished()
}
}
func performTransitionOut(finished: #escaping () -> Void)
{
UIView.animate(withDuration: self.transitionDurationOut, animations: {
self.view.alpha = 0 // fades out correctly
}) { (Finished) in
finished()
}
}
}
Basically, you just implement the protocol in any UIViewController you want, then make a segue of class JFTransitionControllerSegue. In the performTransitionIn function, you can just do something like UIView.animate and change the alpha or whatever you like. The problem I'm having is that the destination segue simply pops up instead of transitioning in properly. From what I can tell while debugging it isn't fully initialized - IBOutlet variables are nil, but the controller itself isn't. Is this a bad design pattern, or am I just missing something simple?
A view controller being initialised is one event. It's view being loaded is another.
The view property of a view controller is loaded lazily and the outlets are built and connected at that point. That's why viewDidLoad() is a thing.
If you want the view to be ready for you, you can call loadViewIfNeeded() on the view controller first.
Misdiagnosed the problem...the destination controller was loaded, but I had forgotten to add the destination controller's view to the window in the Segue class:
class JFTransitionControllerSegue: UIStoryboardSegue {
override func perform() {
let defaultTransitionDuration : CFTimeInterval = 1.5
if let dvc = self.destination as? TransitionController {
dvc.prepareTransitionIn()
}
else {
// Default transition
self.destination.view.alpha = 0
}
if let svc = self.source as? TransitionController {
svc.prepareTransitionOut()
svc.performTransitionOut(){ () in
UIApplication.shared.keyWindow?.insertSubview(self.destination.view, aboveSubview: self.source.view)
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
else
{
// Default transition for the source controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.source.view.alpha = 0
}) { (Finished) in
UIApplication.shared.keyWindow?.insertSubview(self.destination.view, aboveSubview: self.source.view)
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
}
}

Unbalanced calls to begin/end appearance transitions with segmented control

So, I have a segmented control to switch between to view controllers.
However, often, as soon as I switch, I get this message:
Unbalanced calls to begin/end appearance transitions
Plus, after I get this, sometimes it happens that every object on the view disappear.
Here's the code:
func segmentValueChanged(sender: AnyObject) {
if sender.selectedIndex == 0 {
let newController = controllers.newController1
let oldController = childViewControllers.last as UIViewController!
self.container.frame.size.height = newController.view.frame.height
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
transitionFromViewController(oldController, toViewController: newController, duration: 0.9, options: .CurveLinear, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
} else if sender.selectedIndex == 1 {
let newController = controllers.newController2
let oldController = childViewControllers.last as UIViewController!
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
transitionFromViewController(oldController, toViewController: newController, duration: 0.9, options: .CurveLinear, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
}
}
How can I solve this?
This line is backwards:
self.container.frame.size.height = newController.view.frame.height
It is newController's view's frame that you need to set!
Moreover, you are failing to put newController.view into the interface. That is why you end up without an interface.

Passing data in transitionFromViewController

I'm using transitionFromViewController to switch between two view controllers using a segmented control.
Here's my code:
#IBAction func valueChanged(sender: AnyObject) {
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
if viewControllerIdentifiers[sender.selectedSegmentIndex] == "first" {
let vc = newController as! userProfileViewController
vc.userToShow = self.userToShow
}
transitionFromViewController(oldController, toViewController: newController, duration: 0.25, options: .TransitionCrossDissolve, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
}
is it possible to pass data to the childViewControllers, from the Parent?
Yes
transitionFromViewController(oldController, toViewController: newController, duration: 0.25, options: .TransitionCrossDissolve, animations:{ () -> Void in
newController.property = currentProperty
//basically here is where you can pass the data. I used a property as an example. You know what you need to send :)
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})

Resources