質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Cocoa

CocoaはMac OS X用のアプリケーションを構築する為の主要なフレームワークのひとつです。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

303閲覧

cocoaで、ディレクトリ内のファイルを1つずつ処理し、ラベルに現在処理中のファイル名を表示したい

kweet

総合スコア13

Cocoa

CocoaはMac OS X用のアプリケーションを構築する為の主要なフレームワークのひとつです。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2018/11/08 08:12

こんにちは
Swift初心者です。(長年、Microsoft Visual Basic5.0愛用してました)
選択したディレクトリ内の複数のファイルをそれぞれバイナリデータとして
1バイトずつ暗号化するcocoaアプリを作り、
暗号化の動作自体は問題なく出来てるのですが(なのでソフトしては使えてる)
途中経過として、今処理中のファイル名をラベルに表示したいのですが、
ラベルが更新されません。

調べたら、UIの更新はそもそもloop中はできないそうですが、
そうなるとディレクトリ内の複数のファイル名を順次どうやって表示するのか?
が、分からないでいます。
どなたか下記のコードでファイルの暗号化が1つ終わるごとに、
ラベルを更新できる方法を教えていただけないでしょうか?

まだ不慣れなので、実際のコードも全てボタンアクション内に記述してます

該当のソースコード

swift

1 2@IBAction func Start(_ sender: Any) { 3 4 let manager = FileManager() 5 let list = try manager.contentsOfDirectory(atPath: Path) 6 7 //ディレクトリ内のファイルを1つずつ処理 8 for i in stride(from: 0, to: list.count, by: 1) { 9 //入力ファイルの内容をバイナリデータとして読み込み 10 do { 11 let binaryData = try Data(contentsOf: URL, options: []) 12 } catch { 13 print("エラー") 14 } 15 //出力ファイルをオープン 16 let stream = OutputStream(url: OutputFileURL, append: false) 17 //今暗号化しているファイル名をラベルに表示・・・したい 18 Label.stringValue = list[i] 19 20 //バイナリデータを1バイトずつ暗号化 21 for i2 in stride(from: 0, to: filesize, by: 1) { 22 //ここに処理内容を記述してます。長いので割愛。 23 } 24 //終わったら開いたファイルへ出力 25 stream!.write(outputDat, maxLength: outputData.count) 26 } 27 }

試したこと

自分なりにこうすれば良いのかとラベル表示のコード部分を下記の様にやってみましたが
全然違う様でした。

DispatchQueue.main.async { Label.stringValue = list[i] }

補足情報(FW/ツールのバージョンなど)

Xcode 10 swift 4

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

正しく動いているのだとは思いますが、おそらく処理の間レインボーカーソルになっていると思います。
これはRunLoopが止まってしまっているからで、RunLoopが止まっている間は、イベント処理や描画が行われません。

RunLoopは大雑把に言うと

開始

イベント処理(システムイベントや、ユーザーアクションなど)

描画処理

Timer処理

その他の処理

初めに戻る

ということをしています。(実際の処理の順番は非公開のため不明)

@IBAction func Start(_ sender: Any)ということはおそらくボタンクリックなどでこの関数が呼ばれるように設定していると思いますので、この関数はRunLoopのタスクでいうイベント処理部分で実行されています。
見てわかるとおり、イベント処理が終わらない限り、描画処理は行われません。
これを何とかしなければなりません。


その1(僕はあまりお勧めしません)
RunLoopの中でRunLoopを回す

swift

1 Label.stringValue = list[i] 2 3 RunLoop.current.run(until: Date()) // 追加

RunLoopの中でRunLoopを回すというと奇異に感じますが2重のforループのようにするということです。
また、終了時刻に現在の時刻を与えることで、ループが1度回っただけで処理が返ってきます。


その2
時間のかかる処理を別スレッドで行う

swift

1 2/// 暗号化 3private func encrypt(_ completionHandler: () -> Void) { 4 // 5 // func Start(_ sender: Any)の中身だったもの 6 // 7 // ただしここ書き換え 8 // Label.stringValue = list[i] 9 // 描画にかかわりのある処理はメインスレッド上で実行しなければならない 10 DispatchQueue.main.async { Label.stringValue = list[i] } 11 12 // 13 // 14 15 // 追加 16 // 暗号化処理が終わったら与えられた関数を実行する 17 completionHandler() 18} 19 20 21@IBAction func Start(_ sender: Any) { 22 23 // ボタンのクリックなどでアクションが呼ばれると仮定して 24 // 処理中はそれを無効化する 25 let control = sender as? NSControl 26 control?.isEnable = false 27 28 // 別スレッドで暗号化 29 DispatchQueue.global().async { 30 encrypt() { 31 32 // 暗号化処理が終わったらこのクロージャが呼ばれる 33 34 // 描画にかかわりのある処理はメインスレッド上で実行しなければならない 35 DispatchQueue.main.async { 36 // 有効に戻す 37 control?.isEnable = true 38 } 39 } 40 } 41}

投稿2018/11/09 09:59

MasakiHori

総合スコア3384

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kweet

2018/11/09 14:46

非常に分かり易いご回答ありがとうございます!! その2を試したら一発で出来ました!! ファイル名が順次表示される様は壮観でした^^ 何日もググってたのにこんなに早く解決できるとは嬉しい限りです。 今回の回答を元に他のコードについても色々改変すれば、さらにスキルアップ出来そうです。 本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問