asyncAfter not delaying the first execution by specified time












1















I'm trying to animate a textview to get the string characters to appear one by one, then also disappear one by one starting with the first character after a 0.5 second delay.



I am close, the only issue I have is that the very first character gets removed immediately so it's as if it never appeared. Any ideas, here's my function:



extension UITextView {

func animate(newText: String) {

DispatchQueue.main.async {

self.text = ""

for (index, character) in newText.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
self.text?.append(character)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index)) {
self.text?.remove(at: newText.startIndex)
}
}
}
}
}









share|improve this question




















  • 2





    By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

    – Rob
    Dec 31 '18 at 0:35








  • 1





    Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

    – Rob
    Dec 31 '18 at 0:45











  • thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

    – TJMac
    Dec 31 '18 at 1:28
















1















I'm trying to animate a textview to get the string characters to appear one by one, then also disappear one by one starting with the first character after a 0.5 second delay.



I am close, the only issue I have is that the very first character gets removed immediately so it's as if it never appeared. Any ideas, here's my function:



extension UITextView {

func animate(newText: String) {

DispatchQueue.main.async {

self.text = ""

for (index, character) in newText.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
self.text?.append(character)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index)) {
self.text?.remove(at: newText.startIndex)
}
}
}
}
}









share|improve this question




















  • 2





    By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

    – Rob
    Dec 31 '18 at 0:35








  • 1





    Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

    – Rob
    Dec 31 '18 at 0:45











  • thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

    – TJMac
    Dec 31 '18 at 1:28














1












1








1








I'm trying to animate a textview to get the string characters to appear one by one, then also disappear one by one starting with the first character after a 0.5 second delay.



I am close, the only issue I have is that the very first character gets removed immediately so it's as if it never appeared. Any ideas, here's my function:



extension UITextView {

func animate(newText: String) {

DispatchQueue.main.async {

self.text = ""

for (index, character) in newText.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
self.text?.append(character)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index)) {
self.text?.remove(at: newText.startIndex)
}
}
}
}
}









share|improve this question
















I'm trying to animate a textview to get the string characters to appear one by one, then also disappear one by one starting with the first character after a 0.5 second delay.



I am close, the only issue I have is that the very first character gets removed immediately so it's as if it never appeared. Any ideas, here's my function:



extension UITextView {

func animate(newText: String) {

DispatchQueue.main.async {

self.text = ""

for (index, character) in newText.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
self.text?.append(character)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index)) {
self.text?.remove(at: newText.startIndex)
}
}
}
}
}






ios swift animation uitextview grand-central-dispatch






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 31 '18 at 1:16









Rob

299k49557726




299k49557726










asked Dec 30 '18 at 23:53









TJMacTJMac

266




266








  • 2





    By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

    – Rob
    Dec 31 '18 at 0:35








  • 1





    Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

    – Rob
    Dec 31 '18 at 0:45











  • thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

    – TJMac
    Dec 31 '18 at 1:28














  • 2





    By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

    – Rob
    Dec 31 '18 at 0:35








  • 1





    Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

    – Rob
    Dec 31 '18 at 0:45











  • thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

    – TJMac
    Dec 31 '18 at 1:28








2




2





By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

– Rob
Dec 31 '18 at 0:35







By the way, if newText is especially long, you'll find that the first characters will start out evenly, but latter characters will start coming out in spurts. This is "timer coalescing", where distantly scheduled blocks are queued up at the same time to reduce battery drain. You may want to use a different pattern (e.g. a single repeating timer).

– Rob
Dec 31 '18 at 0:35






1




1





Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

– Rob
Dec 31 '18 at 0:45





Do you really mean to add characters every 0.1 seconds and remove them every 0.5 seconds? E.g. after 10 seconds, 100 characters could be added but only 20 of them removed (assuming your string was that long). Is that really the UX you're going for? Or did you mean to add a character every 0.1 seconds and have each character removed 0.5 seconds after it was added (which is not what the above will do).

– Rob
Dec 31 '18 at 0:45













thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

– TJMac
Dec 31 '18 at 1:28





thanks @Rob, this is a good point that I had not considered, I'll probably need to go with a timer to avoid the timer coalescing. I'm still working on the timing of the animation, I do want some sort of an offset, not a direct 1:1. Thanks for the help, much appreciated

– TJMac
Dec 31 '18 at 1:28












1 Answer
1






active

oldest

votes


















1














The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().



Add a constant to the delay:



DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
^^^^^^


This will cause the first character to disappear 1 second later.



Alternatively:



DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {


In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.



var index = 0
let characterArray = Array(newText)

Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
textView.text! += "(characterArray[index])"
index += 1
if index == characterArray.endIndex {
timer.invalidate()
}
}





share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53982398%2fasyncafter-not-delaying-the-first-execution-by-specified-time%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().



    Add a constant to the delay:



    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
    ^^^^^^


    This will cause the first character to disappear 1 second later.



    Alternatively:



    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {


    In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.



    var index = 0
    let characterArray = Array(newText)

    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
    textView.text! += "(characterArray[index])"
    index += 1
    if index == characterArray.endIndex {
    timer.invalidate()
    }
    }





    share|improve this answer






























      1














      The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().



      Add a constant to the delay:



      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
      ^^^^^^


      This will cause the first character to disappear 1 second later.



      Alternatively:



      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {


      In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.



      var index = 0
      let characterArray = Array(newText)

      Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
      textView.text! += "(characterArray[index])"
      index += 1
      if index == characterArray.endIndex {
      timer.invalidate()
      }
      }





      share|improve this answer




























        1












        1








        1







        The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().



        Add a constant to the delay:



        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
        ^^^^^^


        This will cause the first character to disappear 1 second later.



        Alternatively:



        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {


        In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.



        var index = 0
        let characterArray = Array(newText)

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
        textView.text! += "(characterArray[index])"
        index += 1
        if index == characterArray.endIndex {
        timer.invalidate()
        }
        }





        share|improve this answer















        The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().



        Add a constant to the delay:



        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
        ^^^^^^


        This will cause the first character to disappear 1 second later.



        Alternatively:



        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {


        In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.



        var index = 0
        let characterArray = Array(newText)

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
        textView.text! += "(characterArray[index])"
        index += 1
        if index == characterArray.endIndex {
        timer.invalidate()
        }
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 31 '18 at 0:52

























        answered Dec 31 '18 at 0:11









        SweeperSweeper

        66.7k1073139




        66.7k1073139






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53982398%2fasyncafter-not-delaying-the-first-execution-by-specified-time%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Monofisismo

            Angular Downloading a file using contenturl with Basic Authentication

            Olmecas