Skip to content

iOS Trouble Shooting

woongs edited this page Nov 22, 2020 · 5 revisions

iOS Trouble Shooting

@woongs, @kyungpyoda 의 문제 해결 과정 기록입니다.


Autolayout

  • 문제
    • 아래 그림과 같은 레이아웃을 원했으나 원하는 대로 나오지 않음

  • 상황
    • 두 view 사이의 관계를 맺으면 레이아웃이 제대로 잡히질 않음
      • estimated Size: automatic or custom
      • view1:
        • view1.leadting == contentView
        • view1.trailing >= view2.leading + constants
      • view2:
        • view2.trailing == contentView
        • view2.width == 120 or contentView.width * 0.3 or

  • 추측원인

    • collectionView의 estimated size가 automatic으로 되어있을 때 셀이 크기를 자동으로 계산하면서 레이아웃을 제대로 잡지 못하는 듯함.
  • 해결

    • collectionView Cell에서 override func systemLayoutSizeFitting 를 구현해서 직접 원하는 사이즈를 계산해준다.
    • 파라미터로 넘어오는 targetSize는 delegate에서 설정해준 크기 대로 넘어온다.
    • estimatedSize 가 설정되어 있을 때 실제 사이즈를 어느 타이밍에 잡는지 좀 더 공부가 필요하다. delegate 에서 size를 조절해줘도 이후 제약에 따라 변하는 걸 보면 재조정이 들어갈텐데... 이것과 systemLayoutSizeFitting의 관계에 대해 공부가 필요하다.

Animation과 frame

  • 문제

    • 위치를 contentView 왼쪽에 붙여뒀던 editing button이 클릭이 되지 않음.
  • 상황

    • Edit 버튼 클릭시 셀의 contentView를 이동시켜서 왼쪽에 붙여뒀던 editingButton을 보여주게 했는데, editingButton이 클릭되지 않는 문제
    • CGAffineTransform 을 통한 translateX 조절
      • 모습은 정상적으로 보이나 editView의 frame.x 가 그대로 -50
    • contentView frame 조절
      • 모습도 정상적으로 보이고 contentView의 frame.x도 원하는 대로 이동했으나 editingView의 frame은 그대로 -50 -> editingView는 contentView의 subView기 때문에 contentView와의 상대위치인 frame이 그대로 -50인건 당연 -> 근데 왜 터치는 안 되는가.....!!@#!@!#
    • editingView frame 조절
      • contentView는 그대로 있고 editingView만 이동함 (contentView.frame.x 와 editingView.frame.x가 모두 0)
  • 추측 원인

    • editView의 frame이 변하지 않아서 선택이 되지 않는 것 같음 contentView의 frame만 터치가 들어오는 것 같음
  • 해결

    • contentView가 아니라 계속 self로 셀 자체를 옮기고 있었음... ☠️ contentView의 bounds를 옮겨서 해결.
      하지만 여전히 frame을 옮기면 editingView가 아니라 cell이 선택됨 실제로 터치되는 건 frame이 아니라 bounds의 영역인 것으로 보임
    • UX적으로 버튼만이 아니라 셀이 클릭됐을 때 선택되는 게 좋을 것 같아서 그냥 frame을 뷰의 모습만 옮기는 방향으로 구현
    • CollectionViewListCell을 보면 셀 영역 밖에 있다가 옮겨지는 게 아니라 origin은 cell의 origin과 같아 보임. editing mode일 때만 보여지게 하거나 붙이는 방법을 사용하는 듯. 그런식으로 해도 좋을듯 싶다.

UIVew+@IBInspectable 제거

  • UIView의 extension으로 @IBIspectable을 설정해뒀더니 storyboard가 너무 버벅거려서 제거하기로 결정.

    • 필요하다면 특정 View에서만 사용하고 아니면 코드로 구현!
  • 제거했더니 아래와 같은 로그 발생

  • inspectable을 사용하면 아래와 같이 keyPath에 적용되서 사용하게 되는 구조인데, layer의 property에는 저렇게 layer.을 붙여줘야 함.

    • 근데 기존의 코드에서는 get set을 사용해 그걸 변환해주고 있었어서, 그 부분이 빠져서 생기는 문제였음. 그림에서 처럼 layer.을 붙여서 해결
    • cornerRadius나 borderColor도 layer의 프로퍼티인데 이건 layer. 을 붙여주지 않아도 동작하는데 그 이유까지는 잘 모르겠음..

String(describing: self)

  • 문제
    • 아래 예시처럼 클래스의 identifier을 타입 프로퍼티로 갖게 하고 이를 사용할 때가 있다.

      // 콜렉션뷰의 Reusable Cell 생성 시 identifier를 지정해줄때
      collectionView.dequeueReusableCell(withReuseIdentifier: AnyCell.identifier, for: indexPath)
      // 스토리보드 내의 뷰컨트롤러 객체를 identifier로 생성할때
      self.storyboard?.instantiateViewController(identifier: AnyViewController.identifier) as! AnyViewController
    • 나누어 작업하다보니 아래 처럼 두 가지 방법을 섞어 쓰게 되었다.

      static var identifier: String {
        String(describing: self)
      }
      static let identifier: String = String(describing: AnyClass.self)
    • 계산 작업이 덜 생기는, 저장 프로퍼티를 사용하는 static let identifier 로 통일하려고 했다.

      static let identifier: String = String(describing: self)
    • 위 방법으로 시도했는데 아래와 같이 Crash 가 발생했다.

      • image
  • 추측 원인
    • String(describing: self) 부분에서 self 가 함수 그 자체로 들어가게 되어서 identifier 가 제대로 지정되지 않아서 발생한 문제이다.
      • image
    • Computed Property의 대괄호 내에서 self 는 Class.Type 이 반환된다.
      • image
  • 해결
    static let identifier: String = String(describing: AnyClass.self)
    
    • describing 인자로 AnyClass.self 와 같이 클래스 타입을 지정해서 넘겨주는 것으로 해결

segue와 @IBAction을 같이 사용하고 싶다?

  • 문제
    • segue가 달려있는 버튼에 selector도 같이 사용하고 싶은데 동작하지 않음 (filter가 edit 모드에서는 select All로 변함 -> filter는 segue, select All은 action을 하고싶음)
  • 추측원인
    • segue가 달려있으면 다음 화면으로 이동해야 하니 자연스럽게 IBAction과 같은 selector는 무시되는 것으로 보임(지극히 뇌피셜)
  • 해결
    • override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool 를 사용하면 segue 동작을 막을 수 있음
    • 하지만 여전히 IBAction은 동작하지 않음.. 따라서 IBAction은 그냥 사용하지 않고 segue를 막는 부분에서 액션을 실행시켜줌.
    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
        guard currentState != .edit else {
            selectAllIssues()
            return false
        }
        return true
    }

CollectionView select 문제 (feat. select All)

  • 문제

    • select All이 클릭되면
      관리하는 모델(또는 indexPath)를 선택되게 하고
      cellForItemAt 에서 그 상태에 따라 cell.isSelected 값을 바꿔서 처리하려고 했으나,
      이렇게 하고나면 그 다음에 didSelectItemAtdidDeselectItemAt 딜리게이트 메소드가 정상적으로 동작하지 않음
  • 상황:

    • cellForItemAt 에서 cell.isSelected를 바꿔줌
    if selectedIssues.contains(indexPath) {
        cell.isSelected = true
    } else {
        cell.isSelected = false
    }
    • cell의 내부에서는 isSelected 변수에 따라 button의 모습을 바꿔줌
    override var isSelected: Bool {
        didSet {
            editingButton.isSelected = isSelected
        }
    }
  • 추측원인

    • cell 자체의 isSelected 변수는 바뀌었으나, collectionView가 관리하는 selected cell에 셀이 정상적으로 등록되지 않는 것으로 보임.
  • 해결

    • cell.isSelected 뿐만 아니라 collectionView.selectItem까지 처리해줌
    if selectedIssues.contains(indexPath) {
        cell.isSelected = true
        collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .init())
    } else {
        cell.isSelected = false
        collectionView.deselectItem(at: indexPath, animated: false)
    }

performSegue&prepare VS instantiate&present&configure

segue identifer 네이밍 규칙

  • "(현재VC)To(이동할VC)"
"\(elf.self)To\(DestinationVC.self)"
  • extension으로 분리
extension UIViewController {
    func segueIdentifier(to destination: UIViewController.Type) -> String {
        return "\(Self.self)To\(destination.self)"
    }
}

🗂 Project

❗️ Ground Rules

🧩 Trouble Shooting

🗒 API Details

⭐️ Output


🗓 Minutes of meeting
🗓 Daily Scrum
🗓 Peer Session
🗓 Retrospective
Clone this wiki locally