End or Dismiss Lottie Animation View if playOnce is finished in Swift - swift

I already tried both loadingview.removeFromSuperView and loadingView.isHidden = true
Yes, it removes or hides the view, but I can't click on my root view anymore.
I also tried animatonview.background = .forceFinish, but doesn't do the job.
import UIKit
import Lottie
class LoadingAnimationView: UIView {
#IBOutlet weak var loadingView: UIView!
let animationView = AnimationView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
func loadAnimation() {
let animation = Animation.named("success")
animationView.animation = animation
animationView.contentMode = .scaleAspectFill
loadingView.addSubview(animationView)
animationView.backgroundBehavior = .pauseAndRestore
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.topAnchor.constraint(equalTo: loadingView.layoutMarginsGuide.topAnchor).isActive = true
animationView.leadingAnchor.constraint(equalTo: loadingView.leadingAnchor, constant: 0).isActive = true
animationView.bottomAnchor.constraint(equalTo: loadingView.bottomAnchor).isActive = true
animationView.trailingAnchor.constraint(equalTo: loadingView.trailingAnchor, constant:0).isActive = true
animationView.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
animationView.play(fromProgress: 0,
toProgress: 1,
loopMode: .playOnce,
completion: { (finished) in
if finished {
print("Animation Complete")
//please put solution here? dismiss or end loadingView or animationView
} else {
print("Animation cancelled")
}
})
}
EDIT 2:
I'm using the loadingView when the success message is received or 200.
func goOnlineMode(){
APIManager.sharedInstance.fetchServerStatus(completion: { data, error in
if error != nil{
print("Connection Failed")
} else {
if data?.status == 200 || data?.msg == "success" {
print("connected")
loadAnimation(true)
self.setCloudStateValue(value: true)
self.vc.cloudstateChecker()
} else {
print("fail to connect")
}
}
})
}
}
this is my function loading boolean in loadAnimation for Loading the xib.
func loadAnimation(_ display: Bool) {
if (display) {
let window = UIApplication.shared.keyWindow!
if Singleton.animationView == nil {
if let view = Bundle.main.loadNibNamed("LoadingAnimationView", owner: window, options:nil)![0] as? LoadingAnimationView {
Singleton.animationView = view
Singleton.animationView?.frame.size = CGSize(width: window.bounds.width, height: window.bounds.height)
window.addSubview(Singleton.animationView!)
window.layoutIfNeeded()
Singleton.animationView?.loadAnimation()
Singleton.animationView?.translatesAutoresizingMaskIntoConstraints = false
Singleton.animationView?.leftAnchor.constraint(equalTo: window.leftAnchor).isActive = true
Singleton.animationView?.rightAnchor.constraint(equalTo: window.rightAnchor).isActive = true
Singleton.animationView?.topAnchor.constraint(equalTo: window.topAnchor, constant:-60).isActive = true
Singleton.animationView?.bottomAnchor.constraint(equalTo: window.bottomAnchor).isActive = true
window.layoutIfNeeded()
}
}
} else {
if (Singleton.animationView != nil) {
Singleton.animationView?.removeFromSuperview()
Singleton.animationView = nil
}
}
}

Try with this:
Swift 5
animationView.play { (finished) in
animationViewNewOrder!.isHidden = true
}

I solved my problem by using NotificationCenter
Swift 4.2
Add this NotificationCenter Observer in your MainViewController, and also register a Notification.Name to your Constants
NotificationCenter.default.addObserver(self, selector: #selector(removeAnimation(notification:)), name: HIDE_ANIMATION, object: nil)
}
also add this together with your observer
#objc func removeAnimation(notification:NSNotification) {
loadingAnimation(false)
}
I put this Notification Post in my newly created hideAnimation function in LoadingAnimationView.
func hideAnimation() {
NotificationCenter.default.post(name: Notification.Name(HIDE_ANIMATION.rawValue), object: nil)
loadingView.removeFromSuperview()
}
and put the hideAnimation function to your completion finish.

Related

How to properly dispose of Connected Display Window and View?

I have code that opens a window and displays a view on a connected display. My goal is to detect a connection/disconnection of a connected display and show/remove the view accordingly. I have that part working fine.
The problem I am having is closing the window upon disconnection, but then if a subsequent connection is made, and upon creating the window and view again, I get a EXC_BAD_ACCESS error.
I tried a different approach by setting the connectedDisplayWindow and connectedDisplayView to nil, after calling close() on the window when a connected display is removed. Maybe I am misunderstanding the close() method?
Apple Documentation
If the window is set to be released when closed, a release message is sent to the object after the current event is completed. For an NSWindow object, the default is to be released on closing, while for an NSPanel object, the default is not to be released. You can use the isReleasedWhenClosed property to change the default behavior...
Just to make sure, I tried setting the isReleasedWhenClosed to true, but it did not change the problem.
The other thing I see in the console is about seven repeated error strings immediately upon disconnection of the connected display: 2022-04-10 10:28:11.044155-0500 External Display[95744:4934855] [default] invalid display identifier 67EE0C44-4E3D-3AF2-3447-A867F9FC477D before the notification is fired, and one more after the notification occurs: 2022-04-10 10:28:11.067555-0500 External Display[95744:4934855] [default] Invalid display 0x4e801884. Could these be related to the issues I am having?
Full example code:
ViewController.swift
import Cocoa
let observatory = NotificationCenter.default
class ViewController: NSViewController {
var connectedDisplay: NSScreen?
var connectedDisplayWindow: NSWindow?
var connectedDisplayView: NSView?
var connectedDisplayCount: Int = 0
var connectedDisplayID: UInt32 = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupObservatory()
if NSScreen.screens.count > 1 {
handleDisplayConnectionChange(notification: nil)
}
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func viewWillDisappear() {
connectedDisplayWindow?.close()
}
func setupObservatory() {
observatory.addObserver(self, selector: #selector(handleDisplayConnectionChange), name: NSApplication.didChangeScreenParametersNotification, object: nil)
observatory.addObserver(forName: .setupConnectedDisplayWindow, object: nil, queue: nil, using: setupConnectedDisplayWindow)
}
#objc func handleDisplayConnectionChange(notification: Notification?) {
if connectedDisplayCount != NSScreen.screens.count {
if connectedDisplayCount < NSScreen.screens.count {
print("There is a connected display.")
connectedDisplayCount = NSScreen.screens.count
if let _ = NSScreen.screens.last {
if connectedDisplay != NSScreen.screens.last {
connectedDisplayID = NSScreen.screens.last!.displayID!
connectedDisplay = NSScreen.screens.last!
}
} else {
connectedDisplayID = 0
}
if connectedDisplayID != 0 && !connectedDisplayIsActive {
observatory.post(name: .setupConnectedDisplayWindow, object: nil)
}
} else if connectedDisplayCount > NSScreen.screens.count {
print("A connected display was removed.")
connectedDisplayCount = NSScreen.screens.count
connectedDisplayIsActive = false
connectedDisplayWindow?.close()
//connectedDisplayView = nil <- causes error #main in AppDelegate
//connectedDisplayWindow = nil <- causes error #main in AppDelegate
connectedDisplay = nil
connectedDisplayID = 0
}
}
}
func setupConnectedDisplayWindow(notification: Notification) {
if NSScreen.screens.count > 1 && !connectedDisplayIsActive {
connectedDisplay = NSScreen.screens.last
let mask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]
connectedDisplayWindow = NSWindow(contentRect: connectedDisplay!.frame, styleMask: mask, backing: .buffered, defer: true, screen: connectedDisplay) // <- causes error on subsequent connection
connectedDisplayWindow?.level = .normal
connectedDisplayWindow?.isOpaque = false
connectedDisplayWindow?.backgroundColor = .clear
connectedDisplayWindow?.hidesOnDeactivate = false
let viewRect = NSRect(x: 0, y: 0, width: connectedDisplay!.frame.width, height: connectedDisplay!.frame.height)
connectedDisplayView = ConnectedDisplayView(frame: viewRect)
connectedDisplayWindow?.contentView = connectedDisplayView
connectedDisplayWindow?.orderFront(nil)
connectedDisplayView?.window?.toggleFullScreen(self)
connectedDisplayIsActive = true
observatory.post(name: .setupConnectedDisplayView, object: nil)
}
}
}
extension Notification.Name {
static var setupConnectedDisplayWindow: Notification.Name {
return .init(rawValue: "ViewController.setupConnectedDisplayView")
}
static var setupConnectedDisplayView: Notification.Name {
return .init(rawValue: "ConnectedDisplayView.setupConnectedDisplayView")
}
}
extension NSScreen {
var displayID: CGDirectDisplayID? {
return deviceDescription[NSDeviceDescriptionKey(rawValue: "NSScreenNumber")] as? CGDirectDisplayID
}
}
ConnectedDisplayView.swift
import Cocoa
var connectedDisplayIsActive: Bool = false
class ConnectedDisplayView: NSView {
var imageView: NSImageView!
override init(frame: NSRect) {
super.init(frame: frame)
setupObservatory()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupObservatory() {
observatory.addObserver(forName: .setupConnectedDisplayView, object: nil, queue: nil, using: setupConnectedDisplayView)
}
func setupConnectedDisplayView(notification: Notification) {
let imageURL = URL(fileURLWithPath: "/Users/Shared/my image.png")
if let image = NSImage(contentsOf: imageURL) {
imageView = NSImageView(image: image)
imageView.wantsLayer = true
imageView.frame = self.frame
imageView.alphaValue = 1
self.addSubview(imageView)
}
}
}
I commented out the nil settings for the connectedDisplayWindow and connectedDisplayView objects and the error at #main in AppDelegate went away, but then I get an error when trying to reinitialize the connectedDisplayWindow if the connected display is removed or the connection is momentarily interrupted.
The default value of isReleasedWhenClosed is true and connectedDisplayWindow?.close() releases the window. Setting connectedDisplayWindow to nil or to another window releases the window again and causes a crash. Solution: set isReleasedWhenClosed to false.

Target-Action problems with custom view built from standard views

I have a custom view subclassing NSView, which is just an NSStackView containing a label, slider, a second label and a checkbox. The slider and checkbox are both configured to report changes to the view (and eventually, via a delegate to a ViewController):
fileprivate extension NSTextField {
static func label(text: String? = nil) -> NSTextField {
let label = NSTextField()
label.isEditable = false
label.isSelectable = false
label.isBezeled = false
label.drawsBackground = false
label.stringValue = text ?? ""
return label
}
}
#IBDesignable
class Adjustable: NSView {
private let sliderLabel = NSTextField.label()
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let valueLabel = NSTextField.label()
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
var valueFormatter: (Double)->(String) = { String(format:"%5.2f", $0) }
...
#objc func sliderChanged(_ sender: Any) {
guard let slider = sender as? NSSlider else { return }
valueLabel.stringValue = valueFormatter(slider.doubleValue)
print("Slider now: \(slider.doubleValue)")
delegate?.adjustable(self, changedValue: slider.doubleValue)
}
#objc func enabledChanged(_ sender: Any) {
guard let checkbox = sender as? NSButton else { return }
print("Enabled now: \(checkbox.state == .on)")
delegate?.adjustable(self, changedEnabled: checkbox.state == .on)
}
}
Using InterfaceBuilder, I can add one instance of this to a ViewController by dragging in a CustomView and setting it's class in the Identity Inspector. Toggling the checkbox or changing the slider will have the desired effect.
However, if I have multiple instances then in the target-action functions self will always refer to the same instance of the view, rather than the one being interacted with. In other words, self.slider == sender is only true in sliderChanged for one of the sliders. While I can get the correct slider value via sender, I cannot update the correct label as self.valueLabel is always the label in the first instance of the custom view.
Incidentally, #IBDesignable and the code intended to support it have no effect so there's something I'm missing there too - Interface Builder just shows empty space.
The whole file:
import Cocoa
fileprivate extension NSTextField {
static func label(text: String? = nil) -> NSTextField {
let label = NSTextField()
label.isEditable = false
label.isSelectable = false
label.isBezeled = false
label.drawsBackground = false
label.stringValue = text ?? ""
return label
}
}
protocol AdjustableDelegate {
func adjustable(_ adjustable: Adjustable, changedEnabled: Bool)
func adjustable(_ adjustable: Adjustable, changedValue: Double)
}
#IBDesignable
class Adjustable: NSView {
var delegate: AdjustableDelegate? = nil
private let sliderLabel = NSTextField.label()
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let valueLabel = NSTextField.label()
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
var valueFormatter: (Double)->(String) = { String(format:"%5.2f", $0) }
#IBInspectable
var label: String = "" {
didSet {
sliderLabel.stringValue = label
}
}
#IBInspectable
var value: Double = 0 {
didSet {
slider.doubleValue = value
valueLabel.stringValue = valueFormatter(value)
}
}
#IBInspectable
var enabled: Bool = false {
didSet {
enabledCheckbox.isEnabled = enabled
}
}
#IBInspectable
var minimum: Double = 0 {
didSet {
slider.minValue = minimum
}
}
#IBInspectable
var maximum: Double = 100 {
didSet {
slider.maxValue = maximum
}
}
#IBInspectable
var tickMarks: Int = 0
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
setup()
}
override func prepareForInterfaceBuilder() {
setup()
}
override func awakeFromNib() {
setup()
}
private func setup() {
let stack = NSStackView()
stack.orientation = .horizontal
stack.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(sliderLabel)
stack.addArrangedSubview(slider)
stack.addArrangedSubview(valueLabel)
stack.addArrangedSubview(enabledCheckbox)
sliderLabel.stringValue = label
slider.doubleValue = value
valueLabel.stringValue = valueFormatter(value)
slider.minValue = minimum
slider.maxValue = maximum
slider.numberOfTickMarks = tickMarks
// Make the slider be the one that expands to fill available space
slider.setContentHuggingPriority(NSLayoutConstraint.Priority(rawValue: 249), for: .horizontal)
sliderLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
valueLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
addSubview(stack)
stack.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
stack.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
stack.topAnchor.constraint(equalTo: topAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
#objc func sliderChanged(_ sender: Any) {
guard let slider = sender as? NSSlider else { return }
valueLabel.stringValue = valueFormatter(slider.doubleValue)
print("Slider now: \(slider.doubleValue)")
delegate?.adjustable(self, changedValue: slider.doubleValue)
}
#objc func enabledChanged(_ sender: Any) {
guard let checkbox = sender as? NSButton else { return }
print("Enabled now: \(checkbox.state == .on)")
delegate?.adjustable(self, changedEnabled: checkbox.state == .on)
}
}
The solution, as described in the question linked by Willeke, was to ensure init had completed before referencing self. (I'm slightly surprised the compiler allowed it to be used in a property initialiser)
Wrong:
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
Right:
private lazy var slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private lazy var enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))

How to run function multiple times in xcode?

I want to show my interstitial ad after the player have died. Meaning the interstitial ad will load in my game over scene. But i can't just write the function name in the viewdidload section. So is there a way for me to run my interstitialAd function when the player gets to the gameOverScene?
If there was an update function, i would just make a boolean value like so:
var interstitialAdShow = false
and then write this in my didmovetoview:
interstitalAdShow = true
and then in my update function:
If interstitailAdShow == true{
interstitial.present(fromRootViewController: self)
}
But now when there is no update function in the GameViewController, and i can't do it inside of my gameOverScene.swift i can't use this solution, is there another way for me to trigger my interstitial ad function when the gameOverScene comes up.
BTW here is my codes
import UIKit
import SpriteKit
import StoreKit
import GameplayKit
import GoogleMobileAds
var reklameNummer = 0
class GameViewController: UIViewController, GADBannerViewDelegate, GADRewardBasedVideoAdDelegate {
var rewardBaseAd: GADRewardBasedVideoAd!
var interstitial: GADInterstitial!
#IBOutlet weak var bannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
let request2 = GADRequest()
request2.testDevices = [kGADSimulatorID]
bannerView.delegate = self
bannerView.adUnitID = "ca-app-pub-1110799225910030/5762940412"
bannerView.rootViewController = self
//bannerView.load(request2)
interstitial = GADInterstitial(adUnitID: "ca-app-pub-1110799225910030/7460037600")
let request = GADRequest()
interstitial.load(request)
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MenuScene") {
runTheInterStitialAd()
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
rewardBaseAd = GADRewardBasedVideoAd.sharedInstance()
rewardBaseAd.delegate = self
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
//MARK: Video ad
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didRewardUserWith reward: GADAdReward) {
}
func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd:GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad is received.\n")
}
func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Opened reward based video ad.\n")
}
func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad started playing.\n")
}
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
}
func rewardBasedVideoAdWillLeaveApplication(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad will leave application.\n")
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didFailToLoadWithError error: Error) {
//print.text?.append("Reward based video ad failed to load.\n")
}
//MARK: Interstitial ad
func runTheInterStitialAd(){
var runFunc = SKAction.run(showInterstitialAdInScene)
var wait = SKAction.wait(forDuration: 1)
var sequence = SKAction.sequence([wait, runFunc])
}
func showInterstitialAdInScene() {
print("this code is working")
if var scene = SKScene(fileNamed: "Gameoverscene") {
// TRUE
if (interstitial.isReady) == true{
interstitial.present(fromRootViewController: self)
gameNumber = 0
}
// FALSE
if (interstitial.isReady) == false{
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/1033173712")
let request = GADRequest()
interstitial.load(request)
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if rewardBaseAd.isReady{
if reklameNummer == 1{
reklameNummer += 1
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if rewardBaseAd.isReady{
if reklameNummer == 2{
reklameNummer = 0
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if gameNumber == 2{
//showInterstitialAdInScene()
}
}
}
One way to do this is use a callback, which is a way to send the class that created a new class(scene) to that new class(scene) as an argument. This way the new class(scene) can call functions from the old class. Beneath is how to implement this in your code. First I make a custom initialiser which takes in the creating class as an argument and assigns it to a local variable(myCreator). Then in the function called when a player dies(deathFunction()), I call the runIntersitialAd() function from the creating class. I also changed the code which sets up the MenuScene from the GameViewController to send itself(GameViewController class) as an argument inside the MenuScene initialisation.
Change your scene class to something like this:
class MenuScene {
var myCreator : GameViewController!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(fileNamed: String, VCwhoCreatedMe: GameViewController) {
self.init(fileNamed: fileNamed, VCwhoCreatedMe: VCwhoCreatedMe)
myCreator = VCwhoCreatedMe
}
....your other code
deathFunction() { //whatever code you use when the player dies
myCreator.runTheInterStitialAd() //because of the callback construction this is now possible.
...other code
}
}
Inside your GameViewController, function viewDidLoad():
if let view = self.view as! SKView? {
if let scene = MenuScene(fileNamed: "MenuScene",VCwhoCreatedMe: self ) {
runTheInterStitialAd()
scene.scaleMode = .aspectFill
view.presentScene(scene)
..... other code
}
}

Swipe left and right between Core Data with a label

I hope you can help.
There is a label and when any user swipe on it. It will make a call to core data and show values on label. Data will depend upon gesture. If its left/right data will be different on each time. Below is the code that i have written. Please suggest whether its correct or not?
class ViewController: UIViewController {
var helloArray = [Tasks]()
var currentArrayIndex = 0
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var helloLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
leftSwipe.direction = .left
rightSwipe.direction = .right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
ouputData()
}
#objc func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
}
}
func ouputData() {
do {
helloArray = try context.fetch(Tasks.fetchRequest())
for each in helloArray {
helloLabel.text = each.name
}
} catch {
}
appDelegate.saveContext()
}
#IBAction func btnPressed(_ sender: Any) {
let infoTasks = Tasks(context: context)
infoTasks.name = textField.text
appDelegate.saveContext()
do {
try context.save()
} catch {
print("Error")
}
textField.text = ""
}
}
I think you need to modify your handleSwipes and outputData function.
You outputData should only fetch all the data from CD in viewDidload.
Once you have the data source you can fetch item from source and populate your helloLabel based on index.
#objc func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
} else if sender.direction == .right {
currentArrayIndex = (currentArrayIndex - 1) % 3
}
helloLabel.text = helloArray[currentArrayIndex].name
}
And:
func ouputData() {
do {
helloArray = try context.fetch(Tasks.fetchRequest())
} catch {
}
appDelegate.saveContext()
}
Hope it helps.

Swift2: TKSubmitTransitionButton. How do i stop a button from transition/ animating when user login/ signup are incorrect

I have tried a number of times and the best i get is there would be an animation which never cancels or stop regardless of the command i use.
After following #Mattias example, i updated my code and looks something like this:
// DESIGN ANIMATION... TKTRANSITIONSUBMITBUTTON
#IBOutlet weak var btnFromNib: TKTransitionSubmitButton!
#IBAction func onTapButton(sender: AnyObject) {
btnFromNib.startLoadingAnimation()
if let email = self.emailField.text where email != "", let password = self.passwordField.text where password != "" {
DataService.ds.REF_BASE.authUser(email, password: password, withCompletionBlock: { error, authData in
if error != nil {
self.btnFromNib.returnToOriginalState()
if error.code == STATUS_ACCOUNT_NONEXIST {
self.showErrorAlert("This User does not exist", msg: "Please Sign Up")
} else {
}
} else {
self.btnFromNib.startFinishAnimation(1, completion: {
let myTabbarController = self.storyboard?.instantiateViewControllerWithIdentifier("myTabbarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = myTabbarController
myTabbarController.transitioningDelegate = self
})
}
})
}
}
The button yet keeps spinning / animating without stopping. After checking the custom animation class the function inherits from :
public func startLoadingAnimation() {
self.cachedTitle = titleForState(.Normal)
self.setTitle("", forState: .Normal)
self.shrink()
NSTimer.schedule(delay: shrinkDuration - 0.25) { timer in
self.spiner.animation()
}
}
public func startFinishAnimation(delay: NSTimeInterval, completion:(()->())?) {
NSTimer.schedule(delay: delay) { timer in
self.didEndFinishAnimation = completion
self.expand()
self.spiner.stopAnimation()
}
}
public func animate(duration: NSTimeInterval, completion:(()->())?) {
startLoadingAnimation()
startFinishAnimation(duration, completion: completion)
}
public override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
let a = anim as! CABasicAnimation
if a.keyPath == "transform.scale" {
didEndFinishAnimation?()
NSTimer.schedule(delay: 1) { timer in
self.returnToOriginalState()
}
}
}
func returnToOriginalState() {
self.layer.removeAllAnimations()
self.setTitle(self.cachedTitle, forState: .Normal)
}
I noticed it had a public overide func animationDidStop(anim: CAAnimation, finished: Bool) to be the function to stop the animation. But when i use it, i get this error!
How do i rightfully get this to work? ...
Thanks in Advance
** UPDATED QUESTION **
I checked the code of TKTransitionSubmitButton and there are public methods called startLoadingAnimation(), returnToOriginalState() and startFinishAnimation().
I suggest:
Button tapped
startLoadingAnimation()
Check credentials
If wrong/error: returnToOriginalState()
If correct: startFinishAnimation()
Transition, from TKTransitionSubmitButton documentation:
btn.startFinishAnimation {
//Your Transition
let secondVC = SecondViewController()
secondVC.transitioningDelegate = self
self.presentViewController(secondVC, animated: true, completion: nil)
}
Edit: As far as I can see .animate() of the class calls both the start and finish animation, and that's why you can't cancel it.
EDIT2 This one works as intended for me (however I'm not sure about the static cornerRadius)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var submitButton: TKTransitionSubmitButton!
override func viewDidLoad() {
super.viewDidLoad()
submitButton.layer.cornerRadius = 15
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed(sender: AnyObject) {
submitButton.startLoadingAnimation()
delay(2, closure: {
self.checkCredentials()
})
}
func checkCredentials()
{
//FAKING WRONG CREDENTIALS
let userAndPasswordCorrect = false
if !userAndPasswordCorrect
{
submitButton.returnToOriginalState()
//Alert or whatever
}
}
//Faking network delay
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
}
I checked the code of TKTransitionSubmitButton.
I resolve this issue. please try to stop animation under DispatchQueue.main.async and working well.
'func apicallforLogin() {
let urlString = "http://"
guard let requestUrl = URL(string:urlString) else { return }
let request = URLRequest(url:requestUrl)
let task = URLSession.shared.dataTask(with: request) {
(data, response, error) in
if error == nil,let usableData = data {
DispatchQueue.main.async {
self.btnLogin.startFinishAnimation(0.1, completion: {
let HomeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
HomeVC.transitioningDelegate = self
self.navigationController?.pushViewController(HomeVC, animated: false)
})
}
print(usableData)
}else{
DispatchQueue.main.async {
self.btnLogin.returnToOriginalState()
}
}
}
task.resume()'
Well you might have got the answer by now but just I would like to give my answer. You can just copy below code and everything will work fine. I have made the change and had created the pull request for this issue.
import Foundation
import UIKit
#IBDesignable
open class TKTransitionSubmitButton : UIButton, UIViewControllerTransitioningDelegate, CAAnimationDelegate {
lazy var spiner: SpinerLayer! = {
let s = SpinerLayer(frame: self.frame)
return s
}()
#IBInspectable open var spinnerColor: UIColor = UIColor.white {
didSet {
spiner.spinnerColor = spinnerColor
}
}
open var didEndFinishAnimation : (()->())? = nil
let springGoEase = CAMediaTimingFunction(controlPoints: 0.45, -0.36, 0.44, 0.92)
let shrinkCurve = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
let expandCurve = CAMediaTimingFunction(controlPoints: 0.95, 0.02, 1, 0.05)
let shrinkDuration: CFTimeInterval = 0.1
#IBInspectable open var normalCornerRadius:CGFloat = 0.0 {
didSet {
self.layer.cornerRadius = normalCornerRadius
}
}
var cachedTitle: String?
var isAnimating = false
public override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
public required init!(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.setup()
}
func setup() {
self.clipsToBounds = true
spiner.spinnerColor = spinnerColor
}
open func startLoadingAnimation() {
self.isAnimating = true
self.cachedTitle = title(for: UIControlState())
self.setTitle("", for: UIControlState())
self.layer.addSublayer(spiner)
// Animate
self.cornerRadius()
self.shrink()
_ = Timer.schedule(delay: self.shrinkDuration - 0.25) { timer in
self.spiner.animation()
}
}
open func startFinishAnimation(_ delay: TimeInterval,_ animation: CAMediaTimingFunction, completion:(()->())?) {
self.isAnimating = true
_ = Timer.schedule(delay: delay) { timer in
self.didEndFinishAnimation = completion
self.expand(animation)
self.spiner.stopAnimation()
}
}
open func animate(_ duration: TimeInterval,_ animation: CAMediaTimingFunction, completion:(()->())?) {
startLoadingAnimation()
startFinishAnimation(duration, animation, completion: completion)
}
open func setOriginalState() {
self.returnToOriginalState()
self.spiner.stopAnimation()
}
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
let a = anim as! CABasicAnimation
if a.keyPath == "transform.scale" {
didEndFinishAnimation?()
_ = Timer.schedule(delay: 1) { timer in
self.returnToOriginalState()
}
}
}
open func returnToOriginalState() {
self.spiner.removeFromSuperlayer()
self.layer.removeAllAnimations()
self.setTitle(self.cachedTitle, for: UIControlState())
self.spiner.stopAnimation()
self.isAnimating = false
}
func cornerRadius() {
let cornerRadiusAnim = CABasicAnimation(keyPath: "cornerRadius")
// cornerRadiusAnim.fromValue = frame.width
cornerRadiusAnim.toValue = frame.height/2
cornerRadiusAnim.duration = shrinkDuration
cornerRadiusAnim.timingFunction = shrinkCurve
cornerRadiusAnim.fillMode = kCAFillModeForwards
cornerRadiusAnim.isRemovedOnCompletion = false
layer.add(cornerRadiusAnim, forKey: cornerRadiusAnim.keyPath)
}
func shrink() {
let shrinkAnim = CABasicAnimation(keyPath: "bounds.size.width")
shrinkAnim.beginTime = CACurrentMediaTime() + 0.1
shrinkAnim.fromValue = frame.width
shrinkAnim.toValue = frame.height
shrinkAnim.duration = shrinkDuration
shrinkAnim.timingFunction = shrinkCurve
shrinkAnim.fillMode = kCAFillModeForwards
shrinkAnim.isRemovedOnCompletion = false
layer.add(shrinkAnim, forKey: shrinkAnim.keyPath)
}
func expand(_ animation: CAMediaTimingFunction) {
let expandAnim = CABasicAnimation(keyPath: "transform.scale")
expandAnim.fromValue = 1.0
expandAnim.toValue = 26.0
expandAnim.timingFunction = animation
expandAnim.duration = 0.3
expandAnim.delegate = self
expandAnim.fillMode = kCAFillModeForwards
expandAnim.isRemovedOnCompletion = false
layer.add(expandAnim, forKey: expandAnim.keyPath)
}
}