Update NSArrayController correctly - swift3

I'm trying to populate NSTableView using NSArrayController, however can't get it to work. Here is my code:
class AppDelegate: NSObject, NSApplicationDelegate {
private let _spadList: SpadList
var spadList : SpadList {
get { return _spadList }
}
override init() {
_spadList = SpadList()
super.init()
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
createInitialData()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func createInitialData() {
_spadList.chain = "CHAIN"
_spadList.service = "Service"
_spadList.dateString = "2016-12-12"
let firstEquity = Equity()
let anotherEquity = Equity()
firstEquity.name = "Apple"
firstEquity.tradePrice = 12.3
anotherEquity.name = "ORACLE"
anotherEquity.tradePrice = 45.7
_spadList.addEquity(equity: firstEquity)
_spadList.addEquity(equity: anotherEquity)
}
}
And this is ViewController:
class ViewController: NSViewController {
let appDelegate = NSApplication.shared().delegate as! AppDelegate
#IBOutlet weak var tableView: NSTableView!
func equities() -> [Equity]{
return appDelegate.spadList.equities
}
}
Content Array of the NSArrayController is bound to: ViewController.equities
The issue is that my manually created data are not populating itself into my tableView. If I move createInitialData() to ViewController class, they are correctly displayed.
What am I doing wrong?

Related

Updated Reverse Geolocation

There are a number of examples showing how to do reverse geolocation, but nothing recent on implementation in SwiftUI. My current code uses the iPhone GPS to generate coordinates that are used with maps to show the location. I would also like to display the street address since a map without text indicating the location isn't very helpful.
My Questions:
Do I have all the relevant code to implement reverse geolocation?
I have seen examples using storyboards and print statements to display the location, but how do I return the location to a Swiftui view with an #escaping closure?
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var currentAddress: String = ""
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.distanceFilter = 10 // distance before update (meters)
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
}
func startLocationServices() {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
func getLocationCoordinates() -> (Double, Double) {
let coordinate = self.locationManager.location != nil ? self.locationManager.location!.coordinate : CLLocationCoordinate2D()
print("location = \(coordinate.latitude), \(coordinate.longitude)")
return (Double(coordinate.latitude), Double(coordinate.longitude))
}
// Using closure
func getAddress(handler: #escaping (String) -> Void)
{
self.currentAddress = ""
let coordinate = self.locationManager.location != nil ? self.locationManager.location!.coordinate : CLLocationCoordinate2D()
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark?
placeMark = placemarks?[0]
guard let placemark = placemarks?.first else { return }
if let streetNumber = placemark.subThoroughfare,
let street = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
DispatchQueue.main.async {
self.currentAddress = "\(streetNumber) \(street) \(city) \(state)"
}
} else if let city = placemark.locality, let state = placemark.administrativeArea {
DispatchQueue.main.async {
self.currentAddress = "\(city) \(state)"
}
} else {
DispatchQueue.main.async {
self.currentAddress = "Address Unknown"
}
}
}
)
print( self.currentAddress)
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
}
}
// Get Placemark
func getPlace(for location: CLLocation,
completion: #escaping (CLPlacemark?) -> Void) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
guard error == nil else {
print("*** Error in \(#function): \(error!.localizedDescription)")
completion(nil)
return
}
guard let placemark = placemarks?[0] else {
print("*** Error in \(#function): placemark is nil")
completion(nil)
return
}
completion(placemark)
}
}
}
If I add the follow code say in ContentView:
#State private var entryLat: Double = 0.0
#State private var entryLong: Double = 0.0
let result = lm.getLocationCoordinates()
entryLat = result.0
entryLong = result.1
How would I call getPlace?
To use the following code you need to setup the appropriate entitlements and authorizations.
Here is a working example of using geolocation in swiftui, from code I got from
a number of sources on the net years ago.
This should give you a base to do reverse geolocation in swiftui:
import Foundation
import CoreLocation
import SwiftUI
import Combine
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
let locationProvider = LocationProvider()
#State var currentAddress = ""
var body: some View {
Text(currentAddress)
.onAppear {
getAddress()
}
}
func getAddress() {
// for testing Tokyo
let location = CLLocation(latitude: 35.684602, longitude: 139.751992)
locationProvider.getPlace(for: location) { plsmark in
guard let placemark = plsmark else { return }
if let streetNumber = placemark.subThoroughfare,
let street = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
self.currentAddress = "\(streetNumber) \(street) \(city) \(state)"
} else if let city = placemark.locality, let state = placemark.administrativeArea {
self.currentAddress = "\(city) \(state)"
} else {
self.currentAddress = "Address Unknown"
}
}
}
}
/**
A Combine-based CoreLocation provider.
On every update of the device location from a wrapped `CLLocationManager`,
it provides the latest location as a published `CLLocation` object and
via a `PassthroughSubject<CLLocation, Never>` called `locationWillChange`.
*/
public class LocationProvider: NSObject, ObservableObject {
private let lm = CLLocationManager()
/// Is emitted when the `location` property changes.
public let locationWillChange = PassthroughSubject<CLLocation, Never>()
/**
The latest location provided by the `CLLocationManager`.
Updates of its value trigger both the `objectWillChange` and the `locationWillChange` PassthroughSubjects.
*/
#Published public private(set) var location: CLLocation? {
willSet {
locationWillChange.send(newValue ?? CLLocation())
}
}
/// The authorization status for CoreLocation.
#Published public var authorizationStatus: CLAuthorizationStatus?
/// A function that is executed when the `CLAuthorizationStatus` changes to `Denied`.
public var onAuthorizationStatusDenied : ()->Void = {presentLocationSettingsAlert()}
/// The LocationProvider intializer.
///
/// Creates a CLLocationManager delegate and sets the CLLocationManager properties.
public override init() {
super.init()
self.lm.delegate = self
self.lm.desiredAccuracy = kCLLocationAccuracyBest
self.lm.activityType = .fitness
self.lm.distanceFilter = 10
self.lm.allowsBackgroundLocationUpdates = true
self.lm.pausesLocationUpdatesAutomatically = false
self.lm.showsBackgroundLocationIndicator = true
}
/**
Request location access from user.
In case, the access has already been denied, execute the `onAuthorizationDenied` closure.
The default behavior is to present an alert that suggests going to the settings page.
*/
public func requestAuthorization() -> Void {
if self.authorizationStatus == CLAuthorizationStatus.denied {
onAuthorizationStatusDenied()
}
else {
self.lm.requestWhenInUseAuthorization()
}
}
/// Start the Location Provider.
public func start() throws -> Void {
self.requestAuthorization()
if let status = self.authorizationStatus {
guard status == .authorizedWhenInUse || status == .authorizedAlways else {
throw LocationProviderError.noAuthorization
}
}
else {
/// no authorization set by delegate yet
#if DEBUG
print(#function, "No location authorization status set by delegate yet. Try to start updates anyhow.")
#endif
/// In principle, this should throw an error.
/// However, this would prevent start() from running directly after the LocationProvider is initialized.
/// This is because the delegate method `didChangeAuthorization`,
/// setting `authorizationStatus` runs only after a brief delay after initialization.
//throw LocationProviderError.noAuthorization
}
self.lm.startUpdatingLocation()
}
/// Stop the Location Provider.
public func stop() -> Void {
self.lm.stopUpdatingLocation()
}
// todo deal with errors
public func getPlace(for location: CLLocation, completion: #escaping (CLPlacemark?) -> Void) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
guard error == nil else {
print("=====> Error \(error!.localizedDescription)")
completion(nil)
return
}
guard let placemark = placemarks?.first else {
print("=====> Error placemark is nil")
completion(nil)
return
}
completion(placemark)
}
}
}
/// Present an alert that suggests to go to the app settings screen.
public func presentLocationSettingsAlert(alertText : String? = nil) -> Void {
let alertController = UIAlertController (title: "Enable Location Access", message: alertText ?? "The location access for this app is set to 'never'. Enable location access in the application settings. Go to Settings now?", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string:UIApplication.openSettingsURLString) else {
return
}
UIApplication.shared.open(settingsUrl)
}
alertController.addAction(settingsAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
UIApplication.shared.windows[0].rootViewController?.present(alertController, animated: true, completion: nil)
}
/// Error which is thrown for lacking localization authorization.
public enum LocationProviderError: Error {
case noAuthorization
}
extension LocationProvider: CLLocationManagerDelegate {
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.authorizationStatus = status
#if DEBUG
print(#function, status.name)
#endif
//print()
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
self.location = location
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let clErr = error as? CLError {
switch clErr {
case CLError.denied : do {
print(#function, "Location access denied by user.")
self.stop()
self.requestAuthorization()
}
case CLError.locationUnknown : print(#function, "Location manager is unable to retrieve a location.")
default: print(#function, "Location manager failed with unknown CoreLocation error.")
}
}
else {
print(#function, "Location manager failed with unknown error", error.localizedDescription)
}
}
}
extension CLAuthorizationStatus {
/// String representation of the CLAuthorizationStatus
var name: String {
switch self {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
}

Youtubeplayer swift 3 error

Hi I am getting this error message in my xcode project.
//Thread 1: EXC_BAD ACCESS(code = 2,address =DX10f8d3b28)
import UIKit
class PlayerViewController: UIViewController {
#IBOutlet weak var youtubeplayerview:YouTubePlayerView!
var videoURL: URL! = URL(string:"https://www.youtube.com/watch?v=Inn2K-V3NFI")
override func viewDidLoad() {
super.viewDidLoad()
if videoURL != nil {
youtubeplayerview.loadVideoURL(videoURL)
//Thread 1: EXC_BAD ACCESS(code = 2,address =DX10f8d3b28)
} else
{
...
}
// Do any additional setup after loading the view.
}
}
That seems that your YouTubePlayerView - outlet is not connected.
So make it optional to validate
#IBOutlet weak var youtubeplayerview:YouTubePlayerView?
And then
if let myPlayer = youtubeplayerview {
myPlayer.loadVideoURL(videoURL)
} else {
print("na na na ... ")
}
You can also remove the nil check for the videoURL, you declared it as implicitly unwrapped optionals https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
It worked my iboulet was not connected in main storyboard thanks.

TableView crashes under uncaught exception 'NSUnknownKeyException', this class is not key value coding-compliant for the key x.'

I can't seem to make this tableView with custom cells work. I get a runtime error
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]: this class is not key
value coding-compliant for the key causeCampaignDescription.'
The weird thing is that that property is not called like that anymore. This is the cell file MainViewControllerTableViewCell
//
// MainViewControllerTableViewCell.swift
//
//
// Created by on 9/13/17.
// Copyright © 201. All rights reserved.
//
import UIKit
class MainViewControllerTableViewCell: UITableViewCell {
#IBOutlet weak var causeCampaignImageView: UIImageView!
#IBOutlet weak var causeDescription: UILabel!
#IBOutlet weak var daysToFinishLabel: UILabel!
#IBOutlet weak var raisedOverTotalLabel: UILabel!
#IBOutlet weak var percentageCompletedLabel: UILabel!
#IBOutlet weak var goalProgresView: UIProgressView!
//card used on
#IBInspectable var cornerradius : CGFloat = 2
#IBInspectable var shadowOffSetWidth : CGFloat = 0
#IBInspectable var shadowOffSetHeight : CGFloat = 5
#IBInspectable var shadowColor : UIColor = UIColor.black
#IBInspectable var shadowOpacity : CGFloat = 0.5
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func layoutSubviews() {
layer.cornerRadius = cornerradius
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = CGSize(width: shadowOffSetWidth, height: shadowOffSetHeight)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerradius)
layer.shadowPath = shadowPath.cgPath
layer.shadowOpacity = Float(shadowOpacity)
}
}
and this is the view controller that holds the table view MainViewController:
//
// ViewController.swift
//
//
// Created by on 1/28/17.
// Copyright © 2017. All rights reserved.
//
import UIKit
import Alamofire
import SwiftyJSON
import Firebase
class MainViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var campaignRowsData = [CauseCampaign]()
var serverFetchCampaignsUrl = Config.Global._serverUrl
#IBOutlet weak var campaignTableView: UITableView!
//show navigation controller bar
var facebookID = "", twitterID = "",firebaseID = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//hide bar from navigation controller
setToolbar()
campaignTableView.delegate=self
campaignTableView.dataSource=self
campaignTableView.separatorColor = UIColor(white: 0.95, alpha: 1)
recoverUserDefaults()
getCampaignList()
//print(facebookID, twitterID, firebaseID)
}
func setToolbar(){
//hide bar from navigation controller
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.setHidesBackButton(true, animated: false)
self.navigationController?.navigationBar.barTintColor = UIColor.purple
}
func getCampaignList(){
Alamofire.request(serverFetchCampaignsUrl+"/campaigns/get/all/user/\(twitterID)/firebase/\(firebaseID)/cat/0", method: .get).validate().responseJSON { response in
switch response.result {
case .success(let data):
let campaignCausesJSON = JSON(campaignCausesData: data)
self.parseCampaignCausesListResponse(campaignCausesJSON)
//alternative thread operation
DispatchQueue.main.async {
self.campaignTableView.reloadData()
}
case .failure(let error):
print(error)
}
}
}
func parseCampaignCausesListResponse(_ campaignCausesJSON:JSON){
if let activeCampaignCount = campaignCausesJSON["active_campaigns_count"].string {
//Now you got your value
print("TOTAL_ACTIVE_CAMPAIGNS",activeCampaignCount)
CampaignsGlobalDataManagerUtil.campaignTotalCount = Int(activeCampaignCount)!
}
if let contributorUserId = campaignCausesJSON["contributor_user_id"].string {
//Now you got your value
print("CONTRIBUTOR_USER_ID",contributorUserId)
CurrentUserUtil.contributorUserId = contributorUserId
}
if let userTwitterFollowersQty = campaignCausesJSON["user_twitter_followers_qty"].int {
//Now you got your value
print("USER_TWITTER_FOLLOWERS_QTY",userTwitterFollowersQty)
CurrentUserUtil.twitterFollowersCount = Int(userTwitterFollowersQty)
}
//Parsing campaigns object array
campaignCausesJSON["camp_array"].arrayValue.map({
let campaignCause:JSON = $0
parseCampaign(campaignCause)
})
}
//TODO:CHANGE TO DATATAPE OBJECT
func parseCampaign(_ causeCampaign:JSON){
let causeCampaignObject: CauseCampaign = CauseCampaign();
causeCampaignObject.description = causeCampaign["cause_description"].stringValue
causeCampaignObject.id = causeCampaign["campaign_id"].stringValue
if let contributorsQty = causeCampaign["contributors_qty"].int{
causeCampaignObject.contributorsQty = contributorsQty
}
causeCampaignObject.currencySymbol = causeCampaign["currency_symbol"].stringValue
if let currentContributions = causeCampaign["current_contributions"].float{
causeCampaignObject.currentContributions = currentContributions
}
if let goal = causeCampaign["goal"].float {
causeCampaignObject.goal = goal
}
if let goalPercentageAchieved = causeCampaign["goal_percentage_achieved"].float{
causeCampaignObject.goalPercentageAchieved = causeCampaign["goal_percentage_achieved"].float!
}
causeCampaignObject.hashtag = causeCampaign["hashtag"].stringValue
causeCampaignObject.name = causeCampaign["name"].stringValue
if let remainingAmmountToGoal = causeCampaign["remaining_ammount_to_goal"].float{
causeCampaignObject.remainingAmmountToGoal = remainingAmmountToGoal
}
if let picUrl = causeCampaign["pic_url"].stringValue as? String {
causeCampaignObject.picUrl = picUrl
}
if let campaignStartingDate = causeCampaign["created_at"].string{
causeCampaignObject.campaignStartingDate = campaignStartingDate
}
if let campaignEndingDate = causeCampaign["campaign_ending_date"].string{
causeCampaignObject.campaignEndingDate = campaignEndingDate
}
var foundationsArray = [Foundation]()
causeCampaign["foundations"].arrayValue.map({
let id = $0["foundation_id"].stringValue
let twitterUsername = $0["twitter_username"].stringValue
let picPath = $0["pic_path"].stringValue
let name = $0["name"].stringValue
let foundation:Foundation = Foundation(id,twitterAccount: twitterUsername,picPath: picPath,name: name)
foundationsArray.append(foundation)
})
causeCampaignObject.foundations = foundationsArray
campaignRowsData.append(causeCampaignObject)
// foundations = "<null>";
//innecesario
// SACAR DE LA REQUEST INICIAL???
// "went_inactive_date" = "<null>";
// "tweet_id" = 900936910494810112;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return campaignRowsData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = campaignTableView.dequeueReusableCell(withIdentifier: "campaignCell", for: indexPath) as! MainViewControllerTableViewCell
//setting card attributes
print("ROW",campaignRowsData[indexPath.row].description)
let campaignCause:CauseCampaign = campaignRowsData[indexPath.row]
if let desc = campaignCause.description as? String{
cell.causeDescription.text = desc
} else {
print("NULL")
}
return cell
}
func recoverUserDefaults(){
if let fbID = UserDefaults.standard.object(forKey: Config.Global._facebookIdUserDefaults) as? String {
facebookID = fbID
}else{
print("FACEBOOK ID IS NULL")
}
if let twtID = UserDefaults.standard.object(forKey: Config.Global._twitterIdUserDefaults) as? String{
twitterID = twtID
}else{
print("TWITTER ID IS NULL")
}
if let firID = UserDefaults.standard.object(forKey: Config.Global._firebaseIdUserDefaults) as? String{
firebaseID = firID
}else{
print("TWITTER ID IS NULL")
}
return
}
}
The app crashes if the line reloadData is uncommented (I don't even know when and If I should use this)
If I set a label you can't see anything on screen, I see blank cards, but again, as soon as I uncomment reloadData it crashes
There's no causeCampaignDescription, now it's called causeDescription so I don't know why the error keeps mentioning that field
The data desc is ok since I printed it and it has the right content so it's not that
What could be the problem?
Searching the project for causeCampaignDescription will often turn up the offending xib and/or storyboard containing the outdated key path. However, it's been my experience that Xcode is not always 100% reliable about finding things in xibs and storyboards, so if Xcode's search feature won't find it, this command in the Terminal will turn it up straightaway:
find /path/to/your/project/directory -name .git -prune -or -type f -exec grep causeCampaignDescription {} \; -print
Once you find the offending item in the xib or storyboard, change it to the correct string and you should solve your problem.

Swift 3 Create Reminder EKEventStore

I would like to save reminders to the default reminders location. But when I press my button I get a fatal error: unexpectedly found nil while unwrapping an Optional value... I am pretty new to this and most examples I locate are overly complicated or not in Swift 3.
class ViewController: UIViewController {
var eventStore: EKEventStore?
#IBOutlet weak var reminderText: UITextField!
#IBAction func setReminder(_ sender: Any) {
let reminder = EKReminder(eventStore: self.eventStore!)
reminder.title = "Go to the store and buy milk"
reminder.calendar = (eventStore?.defaultCalendarForNewReminders())!
do {
try eventStore?.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
}
}
As its a simple piece of code I thought I would post my answer after I figured it out for any future swifters. I always like simple examples.
import UIKit
import EventKit
class ViewController: UIViewController {
var eventStore = EKEventStore()
var calendars:Array<EKCalendar> = []
// Not used at this time
#IBOutlet weak var reminderText: UITextField!
#IBAction func setReminder(_ sender: Any) {
let reminder = EKReminder(eventStore: self.eventStore)
reminder.title = "Go to the store and buy milk"
reminder.calendar = eventStore.defaultCalendarForNewReminders()
do {
try eventStore.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// get permission
eventStore.requestAccess(to: EKEntityType.reminder, completion:
{(granted, error) in
if !granted {
print("Access to store not granted")
}
})
// you need calender's permission for the reminders as they live there
calendars = eventStore.calendars(for: EKEntityType.reminder)
for calendar in calendars as [EKCalendar] {
print("Calendar = \(calendar.title)")
}
}
override func viewWillAppear(_ animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
With the #adamprocter sample, we also need to add "NSRemindersUsageDescription" key with your message in info.plist file. I tried adding this as a comment but I am not eligible.

transitioningDelegate never called after Segue transition

So I'm trying to implement a custom animation as my app transitions from one View Controller to another, but for some reason the animateTransition function in my custom animation class is never called.
For the record, I'm using Xcode 8 and writing in Swift 3. The problem I'm trying to over come, is that the function is never called - I'll sort out the actual animation in the future, for now its
Here is the code in my CustomPresentAnimationController class, which should handle the transition animation...
import UIKit
class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let duration = 0.5
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
print("Checking duration")
return duration
}
func animationController(forPresented presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 1")
return self
}
func presentationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 2")
return self
}
func animationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 3")
return self
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
print("It's working!")
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {
return
}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {
return
}
let container = transitionContext.containerView
let screenOffDown = CGAffineTransform(translationX: 0, y: -container.frame.height)
let screenOffUp = CGAffineTransform(translationX: 0, y: container.frame.height)
container.addSubview(fromView)
container.addSubview(toView)
toView.transform = screenOffUp
UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
fromView.transform = screenOffDown
fromView.alpha = 0.5
toView.transform = CGAffineTransform.identity
toView.alpha = 1
}) { (success) in
transitionContext.completeTransition(success)
}
}
}
Here is the code for my ViewController (which both of my View Controllers reference)...
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {
override func viewDidLoad() {
if transitioningDelegate != nil {
print("Should do something...")
print(transitioningDelegate)
} else {
print("Transitioing Delegate set to nil")
}
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let customPresentAnimationController = CustomPresentAnimationController()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("doing our custom transition")
print(segue.destination)
let destination = segue.destination
destination.transitioningDelegate = customPresentAnimationController
}
}
When I run the code, and click on the button I provided, which links to my seance View Controller, and is set to 'Present Modally', the view changes with the standard transition (slides up from the bottom) - and the following is printed out to Xcode:
Transitioing Delegate set to nil
doing our custom transition
<moduleView.ViewController: 0x7fe427f09a40>
Should do something...
Optional(<moduleView.CustomPresentAnimationController: 0x60800002e980>)
Obviously the first line is just as the first view loads, all the rest shows that my transitionDelegate is set on the Segue destination, and is indeed loaded in as the second view loads, and that the transitionDelegate is set to CustomPresentAnimationController... yet none of the functions in that class are ever called as it never prints anything out from those functions.
Any help appreciated!
Ensure the method signature for implementing the delegate matches the updated Swift 3 syntax.