2017-11-07 4 views
5

という名前のクラスがあり、変数はfirstNamelastNameです。私はreactCocoaフレームワークを使用してこれらの変数の変更を聞いていますが、didSet{}のようにKVOリッスンのみを使用しているとしましょう。与えられた時間だけ待って最後の関数呼び出しを実行する方法

let firstName:String { didSet{ self.nameDidChange() }} 
let lastName: String { didSet{ self.nameDidChange() }} 

func nameDidChange(){ print("New name:", firstName, lastName} 

私は姓または名のいずれかを変更したいたびに、それは自動的に機能nameDidChangeを呼び出します。だから私はこのコードを持っていると仮定します。 firstNamelastNameの両方を変更すると、nameDidChange関数が2回連続して呼び出されないようにスマートな動きがあるのでしょうか?

のはfirstNameの値が"Anders"で、lastName"Andersson"あるとしましょう、私はこのコードを実行します。

firstName = "Borat" 
lastName = "Boratsson" 

nameDidChangeは二度ここに呼び出されます。最初に"New name: Borat Andersson"、次に"New name: Borat Boratsson"を印刷します。

私の単純な心の中で、私は関数を作成することができます思っていますが、nameIsChanging()のようなものをと呼ばれる、didSetのいずれかが呼び出されるたびにそれを呼び出すと、0.1のような第二のタイマーを起動し、その後コールnameDidChange()が、これらの両方ともdidSetnameIsChangingも呼び出しますので、タイマーは2回進み、両方の時間を起動します。これを解決するために、私は "グローバル" Timerを保持して、それを無効にしてカウントを再起動することができます。しかし、私が解決策を考えると、もっと醜いことが起こります。ここに「ベストプラクティス」はありますか?

+0

デフォルトで 'false'である' isChangingName'を追加し、 'nameDidChange'でそのように設定すると、' nameIsChanging'が 'false'を保証するために' guard'を使用し、 'true'タイマーや 'perform(、with:、afterDelay:) 'で遅延操作を開始してください。 – theMikeSwan

+0

人為的な遅延を追加することによって、ユーザーはそれを「遅い」、または切断したと感じるかもしれません。実際に関数呼び出しを遅らせる必要はありますか?あなたが遅延ルートを行っている場合は、アプリが何かをしている*いくつかの*視覚的な指示があるはずです。 – Zig

+0

@Zig同時に設定されるので、私はそれを防ぐために0.01秒の遅延が必要です。これは、プログラムで同時に設定されている場合のみです。これとは別に、別々に変更されていれば関数を呼び出す必要があります。それは、それぞれが必要でないときにurlリクエストを発生させないようにするためです。 – Sti

答えて

1

あなたは正しい道にいると思います。私は、ユーザーが "停止"タイピングをするまで、名前の変更を呼び出すのを遅らせる必要があると思う。

このような何か:

var timer = Timer() 

var firstName: String = "Clint" { 
    didSet { 
     timer.invalidate() 
     timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in 
      self.nameDidChange() 
     }) 
    } 
} 

var secondName: String = "Eastwood" { 
    didSet { 
     timer.invalidate() 
     timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in 
      self.nameDidChange() 
     }) 
    } 
} 

func nameDidChange() { 
    print(firstName + secondName) 
} 

は、毎回最初または2番目の名前は、それがタイマーを停止し、それが名前の変更をコミットするまで、別の0.2秒を待ちます変更されます。

編集

アダムVenturellaでコメントを読んだ後、私はそれが実際にデバウンス技術であることに気づきました。あなたがそれについてもっと知りたいのであれば、Googleにコンセプトを伝えることは有益でしょう。 nameDidChange機能は一度だけ実行された

changed 

import UIKit 
import PlaygroundSupport 

PlaygroundPage.current.needsIndefiniteExecution = true 

var timer: Timer? = nil 

func nameDidChange() { 
    print("changed") 
} 

func debounce(seconds: TimeInterval, function: @escaping() -> Swift.Void) { 
    timer?.invalidate() 
    timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in 
     function() 
    }) 
} 

debounce(seconds: 0.2) { nameDidChange() } 
debounce(seconds: 0.2) { nameDidChange() } 
debounce(seconds: 0.2) { nameDidChange() } 
debounce(seconds: 0.2) { nameDidChange() } 
debounce(seconds: 0.2) { nameDidChange() } 
debounce(seconds: 0.2) { nameDidChange() } 

出力:ここ

は、概念を説明する簡単な遊び場です。

+1

顔に名前をつけると、この技法は「デバウンス」と呼ばれ、 –

0

私はあなたの質問を正しく理解していませんが、タイマーの代わりにDateを利用して、互いが直ちに発射されたかどうかを調べることができます。また、.timeIntervalSince1970は1970年以降の秒数を返しますので、精度を高めるために100を掛けたものです。

var firstName:String! { didSet{ self.nameDidChange() }} 
var lastName: String! { didSet{ self.nameDidChange() }} 

var currentDate: UInt64 = UInt64((Date().timeIntervalSince1970 * 100)) - 100 

func nameDidChange(){ 
    let now = UInt64(Date().timeIntervalSince1970 * 100) 
    //You can add a higher tolerance here if you wish 
    if (now == currentDate) { 
     print("firing in succession") 
    } else { 
     print("there was a delay between the two calls") 
    } 
    currentDate = now 
} 

EDIT:これは、最後の呼び出しではなく、最初の呼び出しでは動作しませんが、多分これは役立つかもしれない/いくつかのアイデアに

0

の火付け役nameDidChange()への呼び出しを合体する簡単な方法は、経由して、それを呼び出すことですが、そのようなコールが既に保留中でない限り、DispatchQueue.main.async

class MyObject { 
    var firstName: String = "" { didSet { self.scheduleNameDidChange() } } 
    var lastName: String = "" { didSet { self.scheduleNameDidChange() } } 

    private func scheduleNameDidChange() { 
     if nameDidChangeIsPending { return } 
     nameDidChangeIsPending = true 
     RunLoop.main.perform { self.nameDidChange() } 
    } 

    private func nameDidChange() { 
     nameDidChangeIsPending = false 
     print("New name:", firstName, lastName) 
    } 

    private var nameDidChangeIsPending = false 
} 

単一UIイベント(例えば、タッチ)firstNamelastNameに複数の変更をもたらす場合、nameDidChange()は一度だけ呼び出されます。

関連する問題