質問編集履歴

6

誤字

2020/09/14 21:26

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
 
4
4
 
5
+ 本問は解決致しましたので、
6
+
5
7
  [**こちら**](https://teratail.com/questions/291397)に質問を移動しました。
6
8
 
7
9
 

5

誤字

2020/09/14 21:26

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -1,3 +1,13 @@
1
+ ## お願い
2
+
3
+
4
+
5
+ [**こちら**](https://teratail.com/questions/291397)に質問を移動しました。
6
+
7
+
8
+
9
+
10
+
1
11
  ## やりたいこと
2
12
 
3
13
 

4

SearchRootVCの追記

2020/09/14 13:40

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -206,6 +206,374 @@
206
206
 
207
207
 
208
208
 
209
+ ```
210
+
211
+ // SearchRootVC
212
+
213
+
214
+
215
+ import UIKit
216
+
217
+
218
+
219
+ class SearchRootVC: UITableViewController, UISearchBarDelegate {
220
+
221
+
222
+
223
+ @IBOutlet weak var searchBar: UISearchBar!
224
+
225
+
226
+
227
+ var repoToPass: Int!
228
+
229
+
230
+
231
+ var task: URLSessionTask?
232
+
233
+ var repo: [[String: Any]] = []
234
+
235
+
236
+
237
+ override func viewDidLoad() {
238
+
239
+ super.viewDidLoad()
240
+
241
+ setupTableView()
242
+
243
+ tableViewBgImage()
244
+
245
+ }
246
+
247
+
248
+
249
+ // 以下3つ、元から用意されているsearchBar関数名なので、変更NG。
250
+
251
+ func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
252
+
253
+ searchBar.text = ""
254
+
255
+ searchBar.autocapitalizationType = .none // 検索時、先頭を小文字で始める
256
+
257
+ return true
258
+
259
+ }
260
+
261
+
262
+
263
+ func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
264
+
265
+ task?.cancel()
266
+
267
+ }
268
+
269
+
270
+
271
+ func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
272
+
273
+ // キーボード非表示
274
+
275
+ searchBar.resignFirstResponder()
276
+
277
+
278
+
279
+ // MARK: - 不用意なIUOを削除。
280
+
281
+ // let query = searchBar.text!
282
+
283
+ guard let query = searchBar.text, query.isEmpty == false else {
284
+
285
+ print("検索文字がない")
286
+
287
+ return
288
+
289
+ }
290
+
291
+
292
+
293
+ // word, completion, errorHandler 3つの引数をメソッドに渡す。
294
+
295
+ task = SearchAPI.getRandomRepoUrlSession(query, completionHandler: { items in
296
+
297
+ self.repo = items
298
+
299
+ // DispatchQueue で一つ以上のタスクを管理し、async で複数のAPIの非同期通信を実行。
300
+
301
+ DispatchQueue.main.async {
302
+
303
+ // UIを更新する処理
304
+
305
+ self.tableView.reloadData()
306
+
307
+ }
308
+
309
+ }, errorHandler: { error in
310
+
311
+ debugPrint(error?.localizedDescription ?? "")
312
+
313
+ })
314
+
315
+ }
316
+
317
+
318
+
319
+ func setupTableView() {
320
+
321
+ // UISearchBarのdelegateプロパティに、self(=SearchRootVC)を代入。
322
+
323
+ searchBar.delegate = self
324
+
325
+ }
326
+
327
+
328
+
329
+ // MARK: - TableView で背景画像を表示。
330
+
331
+ func tableViewBgImage() {
332
+
333
+ let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.tableView.frame.width, height: self.tableView.frame.height))
334
+
335
+ let image = UIImage(named: "bg_right1")
336
+
337
+
338
+
339
+ imageView.image = image
340
+
341
+ imageView.alpha = 0.8
342
+
343
+
344
+
345
+ self.tableView.backgroundView = imageView
346
+
347
+ }
348
+
349
+
350
+
351
+ // MARK: - // Segueが実行される前に呼び出される。
352
+
353
+ override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
354
+
355
+ // Segueの識別子確認
356
+
357
+ if segue.identifier == Segues.toProfileDetail {
358
+
359
+ // 遷移先ViewCntrollerの取得
360
+
361
+ if let detailVC = segue.destination as? ProfileDetailVC {
362
+
363
+ // 値の設定
364
+
365
+ detailVC.selectedUser = self
366
+
367
+ }
368
+
369
+ }
370
+
371
+ }
372
+
373
+ }
374
+
375
+
376
+
377
+
378
+
379
+ // extension
380
+
381
+
382
+
383
+ extension SearchRootVC {
384
+
385
+
386
+
387
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
388
+
389
+ return repo.count
390
+
391
+ }
392
+
393
+
394
+
395
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
396
+
397
+
398
+
399
+ // dequeueReusableCellで、セルを再利用。
400
+
401
+ // nilを返さない為、オプショナルバインディングは不要。
402
+
403
+
404
+
405
+ // MARK: - セルの再利用
406
+
407
+ let cell = tableView.dequeueReusableCell(withIdentifier: "RepositoryCell", for: indexPath)
408
+
409
+
410
+
411
+ let userRepo = repo[indexPath.row]
412
+
413
+
414
+
415
+ // MARK: - nil回避しつつ、nilの場合は「None」を表示。
416
+
417
+ if let userName = userRepo[ApiKey.userName] as? String {
418
+
419
+ cell.textLabel?.text = userName
420
+
421
+ } else {
422
+
423
+ cell.textLabel?.text = "--None--"
424
+
425
+ }
426
+
427
+ // カスタムクラスの参照を修正。(-> detailTextLabelの表示)
428
+
429
+ if let language = userRepo[ApiKey.language] as? String {
430
+
431
+ cell.detailTextLabel?.text = language
432
+
433
+ } else {
434
+
435
+ cell.detailTextLabel?.text = "--None--"
436
+
437
+ }
438
+
439
+
440
+
441
+ return cell
442
+
443
+ }
444
+
445
+
446
+
447
+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
448
+
449
+ repoToPass = indexPath.row
450
+
451
+ performSegue(withIdentifier: Segues.toProfileDetail, sender: self)
452
+
453
+ }
454
+
455
+ }
456
+
457
+
458
+
459
+ // MARK: - Cellの備忘録
460
+
461
+ // register() ... 用意したviewをcellのテンプレートとして登録するメソッドであり、cellの再利用に必要。
462
+
463
+ // Right Detail を表示は、コード不可 StoryBoard可なので、register()は使用しない。
464
+
465
+
466
+
467
+ // MARK: - クラスメソッドとして定義
468
+
469
+
470
+
471
+ class SearchAPI {
472
+
473
+ // staticとして宣言すると、クラスのインスタンス化が不要。
474
+
475
+ static func getRandomRepoUrlSession(_ query: String, completionHandler completion: @escaping ([[String: Any]]) -> (), errorHandler: @escaping (Error?) -> ()) -> URLSessionTask? {
476
+
477
+
478
+
479
+ // MARK: - 下記の部分はメソッドに渡す前に処理しておく
480
+
481
+ // let word = searchBar.text!
482
+
483
+ // guard let word = searchBar.text, word.isEmpty == false else {
484
+
485
+ // print("検索文字がない")
486
+
487
+ // return
488
+
489
+ // }
490
+
491
+
492
+
493
+ let repositoryUrl = githubBaseUrl + query
494
+
495
+
496
+
497
+ guard let url = URL(string: repositoryUrl) else { return nil }
498
+
499
+
500
+
501
+ let task = URLSession.shared.dataTask(with: url) { (data, responce, error) in
502
+
503
+
504
+
505
+ guard error == nil else {
506
+
507
+ // MARK: - 処理をクロージャに任せる
508
+
509
+ errorHandler(error)
510
+
511
+ return
512
+
513
+ }
514
+
515
+
516
+
517
+ guard let data = data else { return }
518
+
519
+
520
+
521
+ // try!は、例外が発生したときにはクラッシュするので修正。(-> エラーが起こり得ないケースでのみ使用可)
522
+
523
+ // try?で例外を安全に無視できるが、エラーを表示するため do-catch を使用。
524
+
525
+
526
+
527
+ do {
528
+
529
+ let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
530
+
531
+ if let items = json?["items"] as? [[String: Any]] {
532
+
533
+ // MARK: - 処理をクロージャに任せる
534
+
535
+ completion(items)
536
+
537
+ // MARK: - 以下の処理はクロージャに任せる
538
+
539
+ // self.repo = items
540
+
541
+ // // DispatchQueue で一つ以上のタスクを管理し、async で複数のAPIの非同期通信を実行。
542
+
543
+ // DispatchQueue.main.async {
544
+
545
+ // // UIを更新する処理
546
+
547
+ // self.tableView.reloadData()
548
+
549
+ // }
550
+
551
+ }
552
+
553
+ } catch {
554
+
555
+ // MARK: - 処理をクロージャに任せる
556
+
557
+ errorHandler(error)
558
+
559
+ }
560
+
561
+ }
562
+
563
+ task.resume()
564
+
565
+
566
+
567
+ return task
568
+
569
+ }
570
+
571
+ }
572
+
573
+ ```
574
+
575
+
576
+
209
577
  質問は以上です。
210
578
 
211
579
  お時間あるときに、ご返信頂けましたら幸いです????

3

書式の改善。

2020/09/12 03:55

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -8,10 +8,6 @@
8
8
 
9
9
 
10
10
 
11
- (`struct`を使用しているので、「値渡し」?というものでしょうか????)
12
-
13
-
14
-
15
11
  ## エラー
16
12
 
17
13
 

2

誤字

2020/09/12 03:45

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -58,13 +58,11 @@
58
58
 
59
59
  var selectedUser: SearchRootVC!
60
60
 
61
- let repo = selectedUser.repo[selectedUser.RepoToPass]
62
-
63
-
64
-
65
- // static var(let)
61
+ // 以下でエラー
66
-
62
+
67
- //「静的変数」...全てのプログラムが終了するまで、一貫してその値を保持し続ける変数。 初期化は1度のみ。
63
+ let repo = selectedUser.repo[selectedUser.RepoToPass] // Cannot assign to property: 'selectedUser' is immutable
64
+
65
+
68
66
 
69
67
 
70
68
 

1

追記

2020/09/12 03:35

投稿

kazuki_user
kazuki_user

スコア147

test CHANGED
File without changes
test CHANGED
@@ -48,15 +48,61 @@
48
48
 
49
49
 
50
50
 
51
+ import UIKit
52
+
53
+
54
+
55
+ let githubBaseUrl = "https://api.github.com/search/repositories?q="
56
+
57
+
58
+
51
59
  var selectedUser: SearchRootVC!
52
60
 
61
+ let repo = selectedUser.repo[selectedUser.RepoToPass]
62
+
63
+
64
+
53
- // 以下、エラー。
65
+ // static var(let)
66
+
54
-
67
+ //「静的変数」...全てのプログラムが終了するまで、一貫してその値を保持し続ける変数。 初期化は1度のみ。
68
+
69
+
70
+
71
+ struct Segues {
72
+
73
+ static let toProfileDetail = "Detail"
74
+
75
+ }
76
+
77
+
78
+
79
+ struct Identifiers {
80
+
55
- let repo = selectedUser.repo[selectedUser.RepoToPass] // Cannot assign to property: 'selectedUser' is immutable
81
+ static let repositoryCell = "Repository"
56
-
57
-
58
-
82
+
59
- ...
83
+ }
84
+
85
+
86
+
87
+ struct ApiKey {
88
+
89
+ static let language = "language"
90
+
91
+ static let stars = "stargazers_count"
92
+
93
+ static let watchers = "wachers_count"
94
+
95
+ static let forks = "forks_count"
96
+
97
+ static let issues = "open_issues_count"
98
+
99
+ static let userName = "full_name"
100
+
101
+ static let user = "owner"
102
+
103
+ static let imgUrl = "avatar_url"
104
+
105
+ }
60
106
 
61
107
 
62
108
 
@@ -64,7 +110,7 @@
64
110
 
65
111
  static let title = repo[ApiKey.userName] as? String
66
112
 
67
-
113
+
68
114
 
69
115
  static let language = "Written in (repo["language"] as? String ?? "")"
70
116