๊ณต๋ถ€ ์—ฐ์Šต์žฅ :-)

TIL_20180801 ~ 20180804

|

2018.08.01

  • scrollview๊ตฌํ˜„ (paginate, scrolling)
  • HTTP POST

2018.08.02

  • ์–ธ๋ฆฌ์ณ๋ธ”์ƒํƒœ์—์„œ ๋ฆฌ์ณ๋ธ”๋กœ ๋ฐ”๋€Œ๋ฉด StoreVC์˜ viewDidLoad๊ฐ€ ์‹คํ–‰๋˜๋ฉด์„œ bad access์—๋Ÿฌ ๋‚˜๋ฉด์„œ ์•ฑ ์ข…๋ฃŒ
  • ๋ฆฌ์ณ๋ธ”์€ categories๋ฅผ ํ…Œ์ด๋ธ”๋ทฐ์— setํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ถ€๋ฅด๊ธฐ๋•Œ๋ฌธ์— ์•ฑ ์ข…๋ฃŒ ์ธ๋“ฏํ•˜๋‹ค
  • NetworkReachability ์—๋Ÿฌ ๋””๋ฒ„๊น…
    ๋ฆฌ์ณ๋ธ”
    2018-08-03 11:24:01.747084+0900 StoreApp[36028:1961628] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.54.4/UITableView.m:2012
    2018-08-03 11:24:01.756501+0900 StoreApp[36028:1961628] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (8) must be equal to the number of rows contained in that section before the update (8), plus or minus the number of rows inserted or deleted from that section (8 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
    *** First throw call stack:
    (
      0   CoreFoundation                      0x00000001053d61e6 __exceptionPreprocess + 294
      1   libobjc.A.dylib                     0x0000000100f07031 objc_exception_throw + 48
      2   CoreFoundation                      0x00000001053db472 +[NSException raise:format:arguments:] + 98
      3   Foundation                          0x00000001009aa652 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193
      4   UIKit                               0x000000010284bd9b -[UITableView _endCellAnimationsWithContext:] + 19031
      5   UIKit                               0x0000000102865787 -[UITableView endUpdates] + 75
      6   StoreApp                            0x00000001003c3f7f _T08StoreApp0A14ViewControllerC010resetTableC033_5C3AD717C85DC9604A7179854EE8731CLLySay10Foundation9IndexPathVG10indexPaths_tFyycfU_ + 1391
      7   StoreApp                            0x00000001003c7381 _T08StoreApp0A14ViewControllerC010resetTableC033_5C3AD717C85DC9604A7179854EE8731CLLySay10Foundation9IndexPathVG10indexPaths_tFyycfU_TA + 17
      8   StoreApp                            0x00000001003c3fdd _T0Ig_IyB_TR + 45
      9   libdispatch.dylib                   0x000000010641e7ec _dispatch_client_callout + 8
      10  libdispatch.dylib                   0x000000010642b642 _dispatch_sync_thread_bound_invoke + 386
      11  libdispatch.dylib                   0x000000010641e7ec _dispatch_client_callout + 8
      12  libdispatch.dylib                   0x00000001064298cf _dispatch_main_queue_callback_4CF + 628
      13  CoreFoundation                      0x0000000105398c99 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
      14  CoreFoundation                      0x000000010535cea6 __CFRunLoopRun + 2342
      15  CoreFoundation                      0x000000010535c30b CFRunLoopRunSpecific + 635
      16  GraphicsServices                    0x0000000109cb4a73 GSEventRunModal + 62
      17  UIKit                               0x000000010272a057 UIApplicationMain + 159
      18  StoreApp                            0x00000001003c83d7 main + 55
      19  libdyld.dylib                       0x000000010649b955 start + 1
      20  ???                                 0x0000000000000001 0x0 + 1
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException
    (lldb)
    
  • ๋™์ž‘์ˆœ์„œ
    ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ
    ๋ฆฌ์ณ๋ธ” ์ฐํž˜
    viewDidLoad์™„๋ฃŒ
    ๋„คํŠธ์›Œํฌ ๋Š์Œ
    2018-08-03 11:35:23.739963+0900 StoreApp[36565:1986523] [] nw_socket_output_finished shutdown(8, SHUT_WR) [49: Can't assign requested address]
    ์–ธ๋ฆฌ์ณ๋ธ”์ฐํž˜
    ํ…Œ์ด๋ธ”๋ทฐ ์…€ reuseํ–ˆ๋”๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ ์ขŒ๋ผ๋ผ
    ์–ธ๋ฆฌ์ณ๋ธ” ์ƒํƒœ์—์„œ๋Š” json๊ณผ ์บ์‹œํŒŒ์ผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋‹ˆ๊นŒ ๋กœ๋“œ๋Š” ๋˜์ง€๋งŒ ๋„คํŠธ์›Œํฌ ์—์„œ log๋ฅผ ์ฐ๊ธฐ๋•Œ๋ฌธ์— ํ”„๋ฆฐํŠธ๋จ
    
// DataSetter์„ค์ •์‹œ
๋„คํŠธ์›Œํฌ์—๋Ÿฌ: Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={NSUnderlyingError=0x604000259740 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=http://crong.codesquad.kr:8080/woowa/detail/HEDFB, NSErrorFailingURLKey=http://crong.codesquad.kr:8080/woowa/detail/HEDFB, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=50, NSLocalizedDescription=The Internet connection appears to be offline.}

// DataSetter์—์„œ ๊ธฐ๋ณธ detail์ •๋ณด ์„ค์ •ํ• ๋•Œ hash๊ฐ’ ๊ฐ€์ ธ์˜ค๋ฉด์„œ ์—๋Ÿฌ
๋ฐ์ดํ„ฐ ์—๋Ÿฌ!
2018-08-03 11:35:47.513580+0900 StoreApp[36565:1988190] TIC TCP Conn Failed [5:0x604000173740]: 1:50 Err(50)
2018-08-03 11:35:47.514749+0900 StoreApp[36565:1988190] Task <80A0484A-B224-46F5-9065-C9AA9F683E5F>.<12> HTTP load failed (error code: -1009 [1:50])
2018-08-03 11:35:47.517821+0900 StoreApp[36565:1988213] Task <80A0484A-B224-46F5-9065-C9AA9F683E5F>.<12> finished with error - code: -1009

2018.08.03

  • ์Šคํ† ์–ด์•ฑ 8๋‹จ๊ณ„
    • ๋„คํŠธ์›Œํฌ์—ฐ๊ฒฐ์„ฑ์— ๋”ฐ๋ผ statusbar์˜ ์ƒ‰๊น” ์ƒํƒœ ๋ณ€๊ฒฝํ•˜๊ธฐ(์•„์ดํฐ ํ†ตํ™”์ค‘์ผ๋•Œ์ฒ˜๋Ÿผ ํŒŒ๋ž‘ / ๋นจ๊ฐ• ๊นœ๋นก๊นœ๋นก, ๋ฌธ๊ตฌ ์ถ”๊ฐ€)
    • alamofire ์—ฐ๊ฒฐ์„ฑํ™•์ธ ๊ธฐ๋Šฅ ๊ณต๋ถ€(NetworkReachabilityManager)
  • ํ•จ์ˆ˜ํ˜•ํ”„๋กœ๊ทธ๋ž˜๋ฐ: ์†ก์น˜์›๋‹˜ let us go 2018์˜์ƒ

2018.08.04

  • statusbar ํฐ์ƒ‰๊ธ€์”จ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ appdelegate์—์„œ
  • reachability ๊ตฌํ˜„
  • ์งˆ๋ฌธ: layoutsubview๋Š” ์–ธ์ œ๋ถˆ๋ฆฌ๋‚˜์š”
  • ๋ธ”๋กœ๊ทธ ๊ธ€ ์ •๋ฆฌํ•˜ใ„ฑใ…ฃ

TIL_20180723 ~ 20180730

|

2018.07.23

  • ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ reloadํ• ๋•Œ์˜ async๋ฌธ์ œ
    private func resetTableView(indexPaths: [IndexPath]) {
      DispatchQueue.main.sync { [weak self] in // async๋กœ ํ•˜๋ฉด ์—๋Ÿฌ๋‚จ
          self?.tableView.beginUpdates()
          self?.tableView.insertRows(at: indexPaths, with: .automatic)
          self?.tableView.endUpdates()
      }
    }
    
  • beginUpdates()์™€ endUpdates() ์‚ฌ์ด์˜ ์‹œ์ ์—์„œ๋Š” ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์•ˆ๋จ.
  • ํ…Œ์ด๋ธ”๋ทฐ ๋ฐ์ดํ„ฐ์†Œ์Šค๋Š” reload๋‚˜ insert๋ฅผ ํ• ๋•Œ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„(ํ…Œ์ด๋ธ” ๋ทฐ ๋‚ด์˜ ํŠน์ • ์„น์…˜์ด๋‚˜ ์…€)์„ ๋‹ด๋‹นํ•˜๋Š” ๋ชจ๋ธ์ด ๊ฐ™์€ ์ˆ˜์ธ์ง€ ๋‚ด๋ถ€์ ์œผ๋กœ ํ™•์ธํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
    • ์ด๋Š” ๋ณ€๊ฒฝ๋™์ž‘์ด ํ•„์š”์—†๋Š” ๊ณณ์—๋Š” ๋™์ž‘์„ ํ•˜์ง€ ์•Š๊ณ  ๋‚ญ๋น„๋ฅผ ๋ง‰์œผ๋ ค๊ณ  ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•จ.
    • ์œ„์˜ ์ฝ”๋“œ์—์„œ ์•„์˜ˆ ์•ฑ์ด ์ฃฝ๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ
    • insertSection/insertRows, ํ˜น์€ reloadSection/rows๋กœ ์ธํ•ด์„œ ๋ณ€๊ฒฝ๋œ ํ…Œ์ด๋ธ”๋ทฐ์˜ ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ ์ „๊ณผ ๋‹ค๋ฅด๋‹ค๋Š” ์—๋Ÿฌ.
      Terminating app due to uncaught exception 'NSInternalInconsistencyException',
      reason: 'Invalid update: invalid number of sections.  
      The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
      
    • ์—๋Ÿฌ์ž๋ฃŒ ๋งํฌ 1- Error โ€˜Invalid update: invalid number of rows in section 0โ€™ attempting to delete row in table
    • ์—๋Ÿฌ์ž๋ฃŒ ๋งํฌ 2 - Insert rows to UITableView crash
  • ํ•ด๊ฒฐ:
    • ํ™•์ธํ•ด๋ณด๋‹ˆ insertRows์— ์ „๋‹ฌ๋˜๋Š” IndexPath๋Š” ๋ชจ๋‘ ์ž˜ ๋งŒ๋“ค์–ด์ ธ ์ „๋‹ฌ๋˜์—ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ update๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  insertํ•˜๊ณ  update๊ฐ€ ๋๋‚˜๋Š” ๋™์ž‘์ด async๋กœ ์ž‘๋™ํ•˜์—ฌ ๋ชจ๋ธ ๋ณ€๊ฒฝ์ด ์–ธ์ œ ๋˜๋Š”์ง€ ๋ชจ๋ฅด๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.
    • URLSession์œผ๋กœ ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ(async) - 0๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋กœ ํ…Œ์ด๋ธ”๋ทฐ ๊ทธ๋ฆผ - ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ(async) noti๋กœ ํ…Œ์ด๋ธ”๋ทฐ ์—…๋ฐ์ดํŠธ ์‹œ์ž‘ - ๋‹ค๋ฅธ ๋ชจ๋ธ๋„ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์ค‘
    • ํ•ด๋‹น ๋™์ž‘์„ asyncํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋ฉด, URLSession์—์„œ ๋ชจ๋ธ์„ ๋ฐ›์•„์˜ค๋Š”๊ฒŒ ์ „๋ถ€ ์™„๋ฃŒ๋˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ๋‹ค๋ฅธ ์„น์…˜์„ ๋‹ด๋‹นํ•˜๋Š” ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ๋˜๊ณ ์žˆ๋Š” ์ƒํƒœ์—์„œ ๋‹ค๋ฅธ ์„น์…˜์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์š”์ฒญํ•˜๊ณ , ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ ํ•˜๋‹ˆ๊นŒ ์—…๋ฐ์ดํŠธ ์ „ํ›„์˜ ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. (1์„น์…˜์˜ ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ ์žˆ๋Š”๋ฐ 2์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋“ค์–ด์ ธ์„œ ๋˜ 2 ์„น์…˜์„ ์—…๋ฐ์ดํŠธํ•˜๋ผ๊ณ  noti๊ฐ€ ์˜ค๋Š” ์ƒํ™ฉ)
    • beginUpdates()์™€ endUpdates()์‚ฌ์ด์—์„œ ๋ชจ๋ธ๊ณผ ํ…Œ์ด๋ธ”๋ทฐ๊ฐ€ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ์˜ ์ˆ˜๊ฐ€ ๊ฐ™์•„์•ผํ•˜๋Š”๊ฒŒ ํฌ์ธํŠธ! (์—…๋ฐ์ดํŠธ ์ „์˜ ๋ฐ์ดํ„ฐ๊ฐ€ 0๊ฐœ์ด๋ฉด ํ…Œ์ด๋ธ”๋ทฐ์˜ rows๋„ 0, ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์„œ 3๊ฐœ๊ฐ€ ๋˜๋ฉด rows๋„ 3๊ฐœ์—ฌ์•ผํ•˜๋ฉฐ, ์—๋Ÿฌ๋ฉ”์‹œ์ง€์—์„œ๋„ ์–ธ๊ธ‰ํ•˜๊ณ  ์žˆ๋Š” ๋‚ด์šฉ์ด๋‹ค.)
    • ๋”ฐ๋ผ์„œ ๋ชจ๋ธ์€ ๋น„๋™๊ธฐ๋กœ ์—…๋ฐ์ดํŠธ๋œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„, ํ…Œ์ด๋ธ”๋ทฐ ์—…๋ฐ์ดํŠธ๋Š” syncํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋„๋ก ํ•˜์—ฌ ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ์ง๋ ฌ์ ์œผ๋กœ(์ฐจ๋ก€๋Œ€๋กœ) ์—…๋ฐ์ดํŠธ๋˜๊ฒŒํ•˜์—ฌ ๋ชจ๋ธ์—…๋ฐ์ดํŠธ์™€ ํ…Œ์ด๋ธ”๋ทฐ ์—…๋ฐ์ดํŠธ์˜ ํƒ€์ด๋ฐ์„ ๋งž์ถ˜๋‹ค!

2018.07.24

  • ์ฝ์–ด๋ณด๊ธฐ- tableview editing

  • debug

    // DEBUG
    Path1: /Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/tmp/CFNetworkDownload_nEtNzv.tmp
    Path2: /Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/Library/Caches/fdb0d5fcfb86e332505785225a6d9ade.jpg
    ์•„์ดํ…œ์ด๋ฏธ์ง€: https://cdn.bmf.kr/_data/product/HBBCC/fdb0d5fcfb86e332505785225a6d9ade.jpg
    

2018.07.25

downloadTask() ๋™์ž‘๋ฐฉ์‹

  • func downloadTask(with url: URL, completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask
  • ์ด๋ ‡๊ฒŒ URLSession์—์„œ downloadTask๋งŒ๋“ค๋•Œ์š”, ์ผ๋ฐ˜ dataTask๋ž‘์€ ๋‹ค๋ฅด๊ฒŒ ์ปดํ”Œ๋ฆฌ์…˜ ํ•ธ๋“ค๋Ÿฌ์—์„œ data๊ฐ€ ์•„๋‹Œ location(URL)์„ ๋ฐ›๋˜๋ฐ, ์ €๋Š” ์ด task๋ฅผ resume()ํ–ˆ์„๋•Œ ์–ด๋–ป๊ฒŒ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋œ๊ฑด์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹น. ์ปดํ”Œ๋ฆฌ์…˜ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฑฐ์น˜๋ฉด์„œ ์ด๋ฏธ ๋‚ด๋ถ€์ ์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๋Š” location์— ๋œ๊ฑฐ๊ณ  location์ด ๋‹ค์šด๋กœ๋“œ ๋ฐ›์€ ์ž„์‹œํŒŒ์ผ์ด ์ €์žฅ๋œ ๊ฒฝ๋กœ์ธ๊ฑด๊ฐ€์š”? ๊ทธ๋Ÿฌ๋ฉด data๋กœ ๋ฐ”๋กœ ์ ‘๊ทผํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ location์ธ ์ž„์‹œ๊ฒฝ๋กœ๋กœ ๊ฐ€์„œ ๋‹ค์šด๋ฐ›์•„์ง„ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€์•ผํ•˜๋Š”๊ฑด๊ฐ€์š”?
  • Answer: ํŒŒ์ผ๋กœ ์ €์žฅ๋˜๊ณ  ์œ„์น˜๊ฐ€ ๋„˜์–ด์˜ค๋ฉฐ, ํ•ด๋‹น ์œ„์น˜์—๋Š” ์ด๋ฏธ url๋กœ๋ถ€ํ„ฐ ๋‹ค์šด๋ฐ›์•„์ง„ ์ด๋ฏธ์ง€ํŒŒ์ผ์ด ์กด์žฌํ•จ.
  • ์ž„์‹œ๊ฒฝ๋กœ์—๋Š” /Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/tmp/CFNetworkDownload_nEtNzv.tmp ์ด๋Ÿฐ์‹์œผ๋กœ ์ž„์‹œ ํŒŒ์ผ์ด ์ €์žฅ๋จ
// ์ž„์‹œ ํŒŒ์ผ location์œผ๋กœ ๋ฐ”๋กœ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์™€๋ด„
func test() {
       let url = URL(string: "https://cdn.bmf.kr/_data/product/HCCFE/757878b14ee5a8d5af905c154fc38f01.jpg")!

       URLSession.shared.downloadTask(with: url) { (location, response, error) in
           if let error = error {
               print("\(error)")
           }
           if let location = location {
               let img = UIImage(contentsOfFile: location.path)
               DispatchQueue.main.sync {
                   self.view.addSubview(UIImageView(image: img))
               }
           }
       }.resume()
   }

์บ์‹œํด๋”๋กœ ํŒŒ์ผ ์˜ฎ๊ธฐ๊ธฐ

  1. downloadTask์˜ ์ปดํ”Œ๋ฆฌ์…˜ํ•ธ๋“ค๋Ÿฌ์—์„œ location - tmp์ €์žฅ
  2. tmp path์—์„œ cache path๋กœ move
  3. cache์˜ jpgํŒŒ์ผ ์ €์žฅํ•œ ๊ฒฝ๋กœ๋ฅผ UIImage(contentsOf:)๋กœ ๋„˜๊น€
  4. cache ํŒŒ์ผ ์•ˆ์— ์žˆ๋Š”๊ฑฐ ์•ˆ์ง€์šฐ๋ฉด moveError๋‚˜๋Š”๋‹ท!?
do {
        try fileManager.moveItem(at: tmpLocation, to: imageSavingPath)
            let imageData = try? Data(contentsOf: imageSavingPath)
            handler(imageData)
    } catch {
        if FileManager().fileExists(atPath: imageSavingPath.path) {
            let imageData = try? Data(contentsOf: imageSavingPath)
            handler(imageData)
        } else { print("MOVE Error!") }
class ImageSetter {

    class func download(with url: String, handler: @escaping((Data?) -> Void)) {
        let fileManager = FileManager.default
        let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let imageSavingPath = cacheURL.appendingPathComponent(URL(string: url)!.lastPathComponent)

            URLSession.shared.downloadTask(with: URL(string: url)!) { (tmpLocation, response, error) in
                if let error = error {
                    print("Image download Error log: \(error)\n")
                }
                if let response = response as? HTTPURLResponse, response.statusCode == 200, let tmpLocation = tmpLocation {
                    do {
                        try fileManager.moveItem(at: tmpLocation, to: imageSavingPath)
                        let sss = try? fileManager.contentsOfDirectory(atPath: imageSavingPath.path)
                        print(sss) // nil - ํŒŒ์ผ๋ช…๊นŒ์ง€์˜ ๊ฒฝ๋กœ๋ผ nil
                        let fff = try? fileManager.contentsOfDirectory(atPath: cacheURL.path)
                        print(fff)
                        let ddd = fileManager.contents(atPath: imageSavingPath.path)
                        print(ddd) // ์ €์žฅ๋œ ์ด๋ฏธ์ง€์— data์ž์ฒด ํ”„๋ฆฐํŠธ๋จ
                        let imageData = try? Data(contentsOf: imageSavingPath)
                        handler(imageData)
                    } catch {
                        if FileManager().fileExists(atPath: imageSavingPath.path) {
                            let imageData = try? Data(contentsOf: imageSavingPath)
                            handler(imageData)
                        } else { print("MOVE Error!") }
                    }
                }
    }.resume()

}
}

์ด๋ฏธ์ง€ ์„ค์ •

    private func setItemImage(imageURL: String) {
        ImageSetter.download(with: imageURL, handler: { imageData in
            DispatchQueue.main.sync { [weak self] in
//                self.itemImage.image = UIImage(contentsOfFile: imagePath)
                guard let data = imageData else { return }
                self?.itemImage.image = UIImage(data: data)
            }
        })
    }
  1. self.imageView?.image = UIImage(contentsOfFile: imagePath) > ๋ฐ”๋กœ ์•ˆ๋˜๊ณ  ์…€์„ ๋‹ค์‹œ ์žฌ์‚ฌ์šฉํ• ๋•Œ ๊ทธ๋ฆผ ๋œธ
  2. self.itemImage.image = UIImage(contentsOfFile: imagePath)
    • 1,2์˜ ์ฐจ์ด: ์…€์ด ์ฒ˜์Œ ์žฌ์‚ฌ์šฉ๋ ๋•Œ UIImageView ํ”„๋กœํผํ‹ฐ ์ž์ฒด๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ๋งŒ๋“ค์–ด์กŒ๊ธฐ๋•Œ๋ฌธ์—, ์Šคํ† ๋ฆฌ๋ณด๋“œ๋Œ€๋กœ ์ฒ˜์Œ์— ์„ธํŒ…๋˜๊ณ  ๋ฐ์ดํƒ€์†Œ์Šค๊ฐ์ฒด์— ์˜ํ•ด์„œ ์†์„ฑ์ด ํ• ๋‹น๋ ๋•Œ๊ฐ€ ๋˜์„œ์•ผ ์ด๋ฏธ์ง€๊ฐ€ ์„ธํŒ…๋จ(dequeํ• ๋•Œ)

2018.07.26

  • ๋ฐ๋“œ๋ฝ ์ƒํ™ฉ ๋ฐœ๊ฒฌ!
    • main queue์—์„œ ๋˜ main.sync๋กœ ๋Œ์•„๊ฐ€๋Š” ์ฝ”๋“œ๋ธ”๋Ÿญ์„ ํ˜ธ์ถœ
    • setItemImage() ํ•จ์ˆ˜๋Š” ImageSetter์˜ download()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ, ์ธ์ž๋กœ url๊ณผ ํ•จ๊ป˜ ์ปดํ”Œ๋ฆฌ์…˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋„˜๊ธด๋‹ค. (main queue์—์„œ ์‹คํ–‰)
    • ํ•ด๋‹น ์ปดํ”Œ๋ฆฌ์…˜ ํ•ธ๋“ค๋Ÿฌ์˜ ์ฝ”๋“œ ๋ธ”๋Ÿญ์„ ํŽธ์˜์ƒ A๋ธ”๋Ÿญ์ด๋ผ๊ณ  ์นญํ•œ๋‹ค.
    • ์ด๋•Œ A๋ธ”๋Ÿญ์˜ ๊ด€๋ จ worker๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
      • Assignor : setItemImage(), Assignee : download()
    • download()๋Š” ํ•จ์ˆ˜๋ฅผ ๋น ์ ธ๋‚˜์˜ค๋Š” ์‹œ์ ์— A๋ธ”๋Ÿญ์„ ์‹คํ–‰ํ•œ๋‹ค.
    • ์ด๋•Œ์˜ A๋ธ”๋Ÿญ์˜ worker๋Š”
      • Assignor : download(), Assignee : setItemImage()
    • ๋‘ worker๊ฐ€ ๋ชจ๋‘ main queue์—์„œ sync๋กœ ๋™์ž‘ํ•˜๊ธฐ๋•Œ๋ฌธ์—, ์„œ๋กœ๊ฐ€ ๊ต์ฐจํ•˜์—ฌ ์„œ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ ์žˆ๊ณ  ์„œ๋กœ ์ผ์ด ๋๋‚˜๊ธฐ๋งŒ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐ๋“œ๋ฝ ์ƒํ™ฉ์ด ๋ฐœ์ƒ.
    • handler์˜ ์ฝ”๋“œ๋ฅผ async๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฌธ์ œ ์ฝ”๋“œ
    // TableViewCell.swift
    
    private func setItemImage(imageURL: String) {
      // 1. ImageSetter์˜ download()ํ˜ธ์ถœ
      ImageSetter.download(with: imageURL, handler: { imageData in
        // 3. download()ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด์„œ ํ•ธ๋“ค๋Ÿฌ๊ฐ€์‹คํ–‰๋จ. ๊ฐ™์€ main queue๋ผ์„œ ๋ฌธ์ œ๋ฐœ์ƒ  
        DispatchQueue.main.sync { [weak self] in
          guard let data = imageData else { return }
          self?.itemImage.image = UIImage(data: data)
        }
      })
    }
    
// TableViewCell์—์„œ ImageSetter์˜ download๊ฐ€ ํ˜ธ์ถœ

class func download(with url: String, handler: @escaping((Data) -> Void)) {
    let cacheURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
    let imageSavingPath = cacheURL.appendingPathComponent(URL(string: url)!.lastPathComponent)

    // 2. ์•„๋ž˜ ์ค„ ์ฝ”๋“œ์˜ existFile์ด ์ฒดํฌ๋˜๊ณ  handler๊ฐ€ ์‹คํ–‰๋จ
    if let imageData = existFile(at: imageSavingPath) {
        handler(imageData)
    } else {
        URLSession.shared.downloadTask(with: URL(string: url)!) { (tmpLocation, response, error) in
          // do something...
        }.resume()
    }
}

2018.07.27

  • ์Šคํ† ์–ด์•ฑ 7๋‹จ๊ณ„ ์ง„ํ–‰
    • UINavigationController ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๊ณต๋ถ€ (์ƒ์„ธํ™”๋ฉด์—์„œ ์ผ์–ด๋‚˜๋Š” ์•ก์…˜์— ๋”ฐ๋ผ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋ฉ”์†Œ๋“œ ์—ฐ๊ฒฐ)
    • ์Šคํฌ๋กค๋ทฐ ๊ณต๋ถ€
    • ์ƒํ’ˆ ์ƒ์„ธ ์ •๋ณด ๋ฐ›์•„์˜ค๋Š” ๋ชจ๋ธ ๊ฐ์ฒด ์„ค๊ณ„
    • HTTP & Network Basic ์ฑ… ์ฝ์–ด๋ณด๊ธฐ

2018.07.28

  • ๋ถ€์ŠคํŠธ์ฝ”์Šค ๋‚ ์”จ์•ฑ ํ™”๋ฉด ๊ตฌํ˜„ ๋ฏธ์…˜
  • NSDataAsset: ์—์…‹ ์นดํƒˆ๋กœ๊ทธ์—์„œ ์ž๋ฃŒ๋ฅผ ๊ฐ€์ ธ์˜ฌ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐํƒ€์ž…

TIL_20180718 ~ 20180720

|

2018.07.16 ~ 17

  • ํœด๊ฐ€ํœด๊ฐ€ ๋ฌผ๋†€์ด๋ฌผ๋†€์ด :desert_island:

2018.07.18

  • ์Šคํ† ์–ด์•ฑ
    • ํ…Œ์ด๋ธ”๋ทฐ์— ๋“ค์–ด๊ฐ€๋Š” ์ด๋ฒคํŠธ ๋ฑƒ์ง€๋ฅผ ์œ„ํ•œ UILabel ์ปค์Šคํ…€ ํด๋ž˜์Šค ๊ตฌํ˜„

2018.07.19

TODO

  • URLSession ํ•จ์ˆ˜์—์„œ ํด๋กœ์ € ํ˜ธ์ถœ๋˜๋Š” ์ˆœ์„œ
  • ๋„คํŠธ์›Œํฌ ๊ฐ•์˜๋“ฃ๊ณ  ๋„คํŠธ์›Œํ‚นํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ •๋ฆฌ
  • step4 PR๋ณด๋‚ด๊ธฐ(์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ header์— ๋Œ€ํ•œ ์ •๋ณด๋งŒ ๋ฐ›์•„์˜ค๋ฉด ์—…๋ฐ์ดํŠธ๋˜๋Š” ์‹์œผ๋กœ ํ•˜๊ธฐ..)
  • URLSession ๋น„๋™๊ธฐ์ฒ˜๋ฆฌโ€ฆโ€ฆ.

์งˆ๋ฌธ๊ฑฐ๋ฆฌ

  1. Create a session configuration
  2. Create a session (with delegate)
  3. Create Task object with URL
  4. Send request with closure
  5. Wait for response (background)
  6. Wait and receive for data
  7. Finish or Error by closure

session() - 1,2,3 task๋งŒ๋“ค๊ณ  sessionํ•จ์ˆ˜ ์ข…๋ฃŒ task.resume() - 4,5?? viewDidLoad์ข…๋ฃŒ sessionํ•จ์ˆ˜์˜ taskํด๋กœ์ €์‹คํ–‰- ๋‹ค๋ฅธ thread์—์„œ..-6,7? (mainthread์—์„œ๋Š” viewdidLoadํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋์œผ๋ฏ€๋กœ ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€์•Š๋Š”์ค‘) ํด๋กœ์ €์˜ response๋ฐ›๊ณ  ViewController์˜ storeItems ๊ฐ์ฒด์— ๊ฐ’์ด ํ• ๋‹น๋˜๋Š” ๋ธ”๋Ÿญ์—์„œ handler()์‹คํ–‰ ์ด ์ƒํƒœ์—์„œ viewdidLoad๋๋‚˜๋ฉด ๋นˆ ํ…Œ์ด๋ธ”๋ทฐ๋กœ ์ด๋ฏธ ๊ทธ๋ ค์กŒ๊ณ ๊ธฐ๋‹ค๋ฆฌ๋Š”์ค‘์ธ๊ฑด๊ฐ€?

  • viewDidLoad๊ฐ€๋๋‚˜๋ฉด storeItems๊ฐ€ ์—†๋Š” ์ƒํƒœ์—์„œ ํ…Œ์ด๋ธ”๋ทฐ ๊ทธ๋ฆผ
  • ์„ธ์…˜์œผ๋กœ๋ถ€ํ„ฐ storeItems๋ฅผ ๋ฐ›์œผ๋ฉด

2018.07.20

  • ๋™๊ธฐ์™€ ๋น„๋™๊ธฐ
    override func viewDidLoad() {
      doSomething1()
      doSomething2()
      resetTableView()
      doSomething3()
    }
    
    private func resetTableView() {
      DispatchQueue.main.async { [weak self] in
          self?.tableView.reloadData()
      }
    }
    // resetTableView๋Š” reloadData๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋๋‚˜๊ณ  ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์™€์ค‘์— reloadData๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์ผ์„ ํ•˜๊ณ ์žˆ์Œ
    // viewDidLoad()์—์„œ resetTableView์•ˆ์˜ reloadData()๊ฐ€ ์™„๋ฃŒ๋˜์ง€๋„ ์•Š์•˜๋Š”๋ฐ doSomething3์ด ์‹คํ–‰๋œ๋‹ค.   
    
  • mainํ(serial queue)์— self?.tableView.reloadData์ฝ”๋“œ๋ธ”๋Ÿญ์„ ๋„ฃ์Œ
  • async๋ผ๋Š” ๋œป์€ resetTableView(): (worker, but ์ผ์„ ์‹œํ‚ค๋Š”)ํ•จ์ˆ˜๋Š” ํ์— ๋„ฃ์€ ์ฝ”๋“œ๋ธ”๋Ÿญ(์ด ๋˜ํ•œ worker์ง€๋งŒ ์ด ๊ด€๊ณ„์—์„œ๋Š” ์ผ์„ ํ•˜๋Š” ํ•จ์ˆ˜์ž„)์ธ self?.tableView.reloadData์ด ์™„๋ฃŒ๋˜๋“ ๋ง๋“  ์‹คํ–‰๋งŒ ์‹œํ‚ค๊ณ (์ผ๋งŒ ์‹œํ‚ค๊ณ )์ข…๋ฃŒ๋˜๊ฒ ๋‹ค๋Š” ๋œป์ž„
  • ์ผ์„ ์ง€์‹œํ•˜๋Š” ์ชฝ๊ณผ ์ง€์‹œ๋ฐ›์€๊ฑธ ์ฒ˜๋ฆฌํ•˜๋Š” ์ชฝ์˜ ๊ด€๊ณ„๋ฅผ ํ•ญ์ƒ ์ƒ๊ฐํ•˜์ž
  • ์ผ์„ ์ง€์‹œํ•˜๋Š” worker(assignor)๊ณผ ์ผ์„ ์ง€์‹œ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” worker(assignee)๋ฅผ ํŽธ์˜์ƒ ์ €๋ ‡๊ฒŒ ๋ถ€๋ฅธ๋‹ค๊ณ  ๊ฐ€์ •
  • Queue๋Š” ์ผ๋‹จ ์ƒ๊ด€์—†์ด .sync๋กœ ํ˜ธ์ถœํ•˜๋Š”์ง€ .async๋กœ ํ˜ธ์ถœํ•˜๋Š”์ง€๋กœ ๊ตฌ๋ถ„ํ•˜์ž.
  • ๋น„๋™๊ธฐ, async: assignor๋Š” assignee์—๊ฒŒ ์ผ์„ ์‹œํ‚ค๊ณ  ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋”ฐ๋ผ์„œ assignor๊ฐ€ ํ•˜๋Š” ๋‹ค๋ฅธ ๋งŽ์€ ์ผ๋“ค๊ณผ ํ•จ๊ป˜ ๋ณด๋ฉด ์–ด๋–ค๊ฒŒ ๋จผ์ € ์™„๋ฃŒ๋ ์ง€ ๋ณด์žฅ ํ•  ์ˆ˜ ์—†๋‹ค.
  • ๋™๊ธฐ, sync: assignor๋Š” assignee์—๊ฒŒ ์ผ์„ ์‹œํ‚ค๊ณ  ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด๋•Œ๋Š” assignor๊ฐ€ ํ•˜๋Š” ๋‹ค๋ฅธ ๋งŽ์€ ์ผ๋“ค๊ณผ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ๋‹ค.
    • ๋‹ค๋งŒ ์ฃผ์˜ํ•  ์ ์€ main queue์—์„œ syncํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ ํ•  ๋•Œ์ธ๋ฐ, assignor๊ฐ€ ๋ฉ”์ธํ์—์„œ ๋˜ ๋‹ค๋ฅธ assignee์—๊ฒŒ ๋ฉ”์ธํ์—์„œ syncํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋„๋ก ํ˜ธ์ถœํ•˜๋ฉด ์„œ๋กœ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ๋งŒ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐ๋“œ๋ฝ ์ด๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ์Œ.
  • Queue: Concurrent / Serial ํ๋Š” ์ข€ ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ์ž„
  • serial: ์‹ฑ๊ธ€์Šค๋ ˆ๋“œ, Concurrent: ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ
  • serial์€ ์‹ฑ๊ธ€์Šค๋ ˆ๋“œ์ด๊ธฐ๋•Œ๋ฌธ์—, ๋ฐ๋“œ๋ฝ ๋ฐœ์ƒ์˜ ์œ„ํ—˜์ด ์žˆ์Œ

TIL_20180709 ~ 20180713

|

2018.07.09

  • ์Šคํ† ์–ด์•ฑ step1 ๋จธ์ง€
  • ์Šค์œ„ํ”„ํŠธ ์ƒ์† ๊ณต์‹๋ฌธ์„œ ์ฝ๊ธฐ
  • class์™€ static๋ฉ”์„œ๋“œ ์ฐจ์ด
    • ๊ทธ๋ฆฌ๊ณ  class ๋ฉ”์„œ๋“œ๋Š” ์˜ค๋ฒ„๋ผ์ด๋”ฉ ๊ฐ€๋Šฅ.
    • class ๋ฉ”์„œ๋“œ๋Š” static ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์†ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“  ๋ฉ”์„œ๋“œ

2018.07.10

  • UIKit์—์„œ ์˜ค๋ฒ„๋ผ์ด๋“œ ์‚ฌ์šฉํ•˜๋Š”์ด์œ 
    • ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 
        1. ์„œ๋ธŒํด๋ž˜์Šค์—์„œ ์Šˆํผํด๋ž˜์Šค์˜ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ณ  ์„œ๋ธŒ์—์„œ ์ถ”๊ฐ€๊ธฐ๋Šฅ ๊ตฌํ˜„
        1. ์Šˆํผํด๋ž˜์Šค์˜ ๊ธฐ๋Šฅ ์“ฐ์ง€ ์•Š๊ณ  ์•„์˜ˆ ์žฌ์ •์˜
    • ์ƒ์†์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ชฉ์ ์€ super์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ์œ„ํ•ด์„œ์ธ๋ฐ, UIKitํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ์ปค์Šคํ…€ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ, superํด๋ž˜์Šค์— ํ•„์ˆ˜์ ์ธ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋˜์–ด์žˆ์„ ์ˆ˜ ์žˆ๊ณ , ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๊ฐ€ empty์ด๋”๋ผ๋„ ๋ทฐ ๊ณ„์ธต์ด ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ์ปจํ…Œ์ด๋„ˆ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์˜ ๊ฒฝ์šฐ super์˜ super, ๊ทธ super์ธ ์ฒด์ธ์„ ๋”ฐ๋ผ๊ฐ€์„œ ๊ฑฐ๊ธฐ์— ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š”์ง€, ํ˜น์€ ๋‚ด๊ฐ€ ๋‹ค ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณด์žฅํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ super๋ฅผ ํ•ญ์ƒ ๋ถˆ๋Ÿฌ์˜ค๋Š”๊ฒŒ ๋งž๋‹ค.
    • UIKit์˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฒฝ์šฐ override๋ฉ”์†Œ๋“œ์—์„œ super๋ฅผ ๊ผญ ํ˜ธ์ถœํ•˜๋„๋ก ํ•˜๋ผ๋Š” ์ด์œ ๋Š” ์œ„์™€ ๊ฐ™์€ ๋งฅ๋ฝ์—์„œ์ด๋‹ค.
  • ํด๋ž˜์Šค ์ƒ์†๊ณผ ์ด๋‹ˆ์…œ๋ผ์ด์ € ์žฌ์ •์˜
  • ์Šคํ† ์–ด์•ฑ 2๋‹จ๊ณ„

2018.07.11

  • ๋ถ€์ŠคํŠธ์ฝ”์Šค ํšŒ์›๊ฐ€์ž…ํ™”๋ฉด์œผ๋กœ ์˜คํ† ๋ ˆ์ด์•„์›ƒ ์—ฐ์Šต.. ์Šคํ† ๋ฆฌ๋ณด๋“œ ์‚ฌ์šฉ
  • UIImagePickerControllerEditedImage ์‚ฌ์šฉ์œผ๋กœ ๋ณ€๊ฒฝ
  • textfield.text๋Š” ์•„๋ฌด๊ฒƒ๋„ ์ž…๋ ฅ๋˜์ง€ ์•Š์•˜์„๋•Œ ๊ฐ’์ด nil์ด ์•„๋‹ˆ๋ผ โ€œโ€œ์ธ ๊ฒƒ๊ฐ™์Œ. ์ด์œ ๊ฐ€..?
  • touchesBegan์ด GestureRecognizer๊ฐ€ ์”Œ์›Œ์ง„ ๋ทฐ๋ฅผ ํ„ฐ์น˜ํ•ด๋„ ํ˜ธ์ถœ๋˜๋Š”์ด์œ โ€ฆ

2018.07.12

  • 11์ผ์— ๊ณต๋ถ€ํ•œ ์งˆ๋ฌธ๊ฑฐ๋ฆฌ ์งˆ๋ฌธํ•˜๊ธฐ
  • step3๋จธ์ง€!
  • xibํŒŒ์ผ ์ด์šฉํ•ด์„œ ์ปค์Šคํ…€ ํ—ค๋” ๋ทฐ ๋งŒ๋“ค๊ณ  ์ ์šฉํ•จ
    • https://www.youtube.com/watch?v=gEoiiZZE6cw
  • step4 PR๋ณด๋ƒ„
  • xibํŒŒ์ผ์˜ load๋ฉ”์„œ๋“œ์˜ owner์˜๋ฏธ
  • Concurrent Programming๊ฐ•์˜ ์ •๋ฆฌ
  • ํ”ผ๋“œ๋ฐฑ ์ •๋ฆฌ
  • Toaster๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ† ์–ด์•ฑ์— ์ ์šฉ

2018.07.13

  • step4 ๋จธ์ง€!
  • xibํŒŒ์ผ๋กœ ํ—ค๋” ๋งŒ๋“ค๊ธฐ -> ์ปค์Šคํ…€ ์…€ ํ”„๋กœํ† ํƒ€์ž…์œผ๋กœ ํ—ค๋” ๋งŒ๋“ค๊ธฐ๋กœ ๋ณ€๊ฒฝ
    • xib์„ ๋ถˆ๋Ÿฌ์˜ฌ๋•Œ๋Š” ํ•ญ์ƒ required init(coder:)๋ฅผ ๊ผญ ๊ตฌํ˜„ํ•ด์ค˜์•ผํ•œ๋‹ค!
  • step5ํ•˜๊ธฐ์ „์— ๋„คํŠธ์›Œํฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ณต๋ถ€ํ•˜๊ธฐ
    • ๋ถ€์ŠคํŠธ์ฝ”์Šค ์ž๋ฃŒ ์ฐธ๊ณ 
    • URLSession, URLSessionTask
    • https://hcn1519.github.io/articles/2017-07/iOS_URLSession
    • https://www.raywenderlich.com/158106/urlsession-tutorial-getting-started

Handling UIKit Gestures ๋ฒˆ์—ญ

|

ํ˜ผ์ž ๊ณต๋ถ€&์ •๋ฆฌ์šฉ์œผ๋กœ ์• ํ”Œ ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ Handling UIKit Gestures Article ๋ฒˆ์—ญ

์ฐธ๊ณ ๋งํฌ

Handling UIKit Gestures

GestureRecognizer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ„ฐ์น˜๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ธ๋“ค๋งํ•˜๊ณ  ์ผ๊ด€์ ์ธ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(user experience๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Overview

  • GestureRecognizer๋Š” ํ„ฐ์น˜๋‚˜ ํ”„๋ ˆ์Šค ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ํ•˜๋‚˜ ์ด์ƒ์˜ GestureRecognizer๋ฅผ ์–ด๋–ค ๋ทฐ์—๋„ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.
  • GestureRecognizer๋Š” ์ง„ํ–‰๊ณผ ์ด๋ฒคํŠธ ํ•ด์„์— ํ•„์š”ํ•œ ๋ชจ๋“  ๋กœ์ง์„ ์บก์Šํ™”ํ•˜๊ณ  ํŒจํ„ด(์ด๋ฒคํŠธ ์ข…๋ฅ˜)์— ์ผ์น˜์‹œํ‚จ๋‹ค. (match them to a known pattern. )
  • ๋งž๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด, GestureRecognizer๋Š” ํƒ€๊ฒŸ ๊ฐ์ฒด์— ๋…ธํ‹ฐ๋ฅผ ๋ณด๋‚ธ๋‹ค.
    • (Gesture recognizer notifying its target)
  • ์œ„์—์„œ ๋งํ•˜๋Š” ํƒ€๊ฒŸ๊ฐ์ฒด๋Š” viewController์ผ์ˆ˜๋„, view์ผ์ˆ˜๋„, ํ˜น์€ ์•ฑ ๋‚ด์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์ผ์ˆ˜๋„์žˆ๋‹ค.
  • Action: ํ•ด๋‹น ์ œ์Šค์ณ๊ฐ€ ๊ฐ์ง€๋์„๋•Œ ํ˜ธ์ถœ๋  ๋ฉ”์„œ๋“œ
  • Target: ์ œ์Šค์ณ๋ฅผ ์ฒ˜๋ฆฌํ•  ํƒ€๊ฒŸ ๊ฐ์ฒด

action methods

  • GestureRecognizer๋Š” target-action ๋””์ž์ธํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ noti๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”๋‹ค.
  • ์˜ˆ๋ฅผ๋“ค์–ด UITapGestureRecognizer๊ฐ€ ์ ์šฉ๋œ ๋ทฐ์—์„œ single-finger tap์ด ๊ฐ์ง€๋˜๋ฉด, ์•ก์…˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
  • GestureRecognizer์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ• ๋•Œ ์•ก์…˜๋ฉ”์„œ๋“œ๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์คŒ
    • ex) let panGesture = UIPanGestureRecognizer(target: self, action: #selector(cardDragged(_:)))
    • ์•ก์…˜๋ฉ”์„œ๋“œ: ํ•ด๋‹น ์ œ์Šค์ณ๊ฐ€ ๊ฐ์ง€๋์„๋•Œ ํ˜ธ์ถœ๋  ๋ฉ”์„œ๋“œ
  • ์•ก์…˜๋ฉ”์„œ๋“œ๋Š” UIGestureRecognizer sender๊ฐ์ฒด๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.
  • ํ•ด๋‹น sender๊ฐ์ฒด๋กœ ์ œ์Šค์ณ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

State property

  • GestureRecognizer์˜ ์•ก์…˜๋ฉ”์†Œ๋“œ๋Š” ์ œ์Šค์ณ์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Continuous Gesture
    • .began
    • .changed
    • .ended
    • .cancelled