Skip to content

Day 16 개발일지 iOS

정재명(Jae-Myeong) edited this page Dec 4, 2020 · 4 revisions

Geometry Reader

기기마다의 사이즈를 어떻게 조절하나 했는데 이런게 있었다.

Frame View 본인의 height, width. 사이즈를 지정할 때

Padding View 의 top, bottom, leading, trailing 를 지정할 때

Spacer Stack 에 소속된 View들 사이의 일정한 거리를 지정할 때

GeometryReader View의 부모뷰의 사이즈를 기준으로 본인의 사이즈를 조절할 때

What is Geometry Reader in SwiftUI?

[SwiftUI] GeometryReader 내부에 변수 선언하기

Device-specific Layout with SwiftUI on Apple Watch and iPhone


중복되는 Style 속성 따로 빼서 작성하기

이렇게 빼고

struct RedRoundButton: ButtonStyle {

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding(40)
            .font(.title)
            .background( Circle()
                .fill(Color.red))
    }

}

이렇게 사용한다.

Button("Button") {}
    .buttonStyle(RedRoundButton())

참고


리스트 백그라운드 변경

참고링크 를 보고 여러 리스트에서 하나의 이미지만 백그라운드를 변경하고 싶었는데 사용하지 못 했다.

List { 
    ForEach(0..<10) { Text("Row \($0)")
    }.listRowBackground(Color.red) }

그 대신 colorMultiply를 사용해서 이미지의 foreground를 변경했다.


토큰의 정보를 수정하는데, 업데이트가 되지 않는 현상

이 문제는 EnvironmentObject 탓인가? 라고 여겨져 ObservedObject로 변경하는 로직으로 다시 짰는데, 이 문제가 아니었다...ㅎ

Timer때문인 것 같다.


화면 터치하면 키보드 내리기

hideKeyboard를 할 때 키보드 제외 뷰를 터치하면 작동시키게 했다. 이때, 뷰에 탭 제스쳐를 달아 작성했는데, 세그먼트 뷰가 터치되지 않았다. 이를 세그먼트 뷰에도 탭 제스처를 달아 해결했다.


루트뷰로 돌아가기

큐알 찍고 편집 화면 갔다가 편집 취소하거나 편집 완료하면 바로 루트뷰로 돌아와야 한다. 이를 위해 아래에서 설명해준 방법을 사용했다.

궁금: isDetailLink가 뭐지?


environmentObject

슈퍼뷰에서 environmentObject를 생성해두면 하위 뷰에서는 아무런 코드를 작성하지 않아도 이 객체를 사용할 수 있을 줄 착각했다. 그래서 꽤 오래 시간을 잡아먹었는데, environmentObject를 사용하는 하위뷰에서는 꼭 environmentObject 프로퍼티를 선언하자.


QRCode를 찍고 편집화면으로 넘어갔다가 다시 QRCode 카메라뷰로 오는 현상

QRScannerView를 sheet로 띄웠더니 해결 되었다..!

시트로 띄우고 시트를 날릴 때는 아래 방법을 사용해서 했다.

@Environment(\.presentationMode) var mode: Binding<PresentationMode>

mode.wrappedValue.dismiss()


TokenEditView에 Service만 넘기기

QRGuide뷰에서 TokenEditView를 호출할 때 Service 객체를 전달해주어야한다. 그런데 QRGuide뷰에서는 Service가 필요 없다. 이럴 때 어떻게 하면 좋을지 고민이 되었다. 이런 상황에 사용하라고 @EnvironmentObject라는 프로퍼티래퍼를 제공해주는 것 같지만, Service는 MainViewModel이 들고 있기 때문에 적절해보이지 않았다. 그래서 Service 객체만 QRGuide에 넘겨주고 다시한번 TokenEditView에 전달해주기로 했다.

메인셀이 작은 셀로 돌아올 때 첫번째 셀로 돌아가기

처음에 아래와 같이 변경하였었는데 버그가 발생했따.

func updateMainTokenIndex(id: UUID) {
  let lastMainIndex = mainTokenIndex
	tokens.insert(tokens.remove(at: lastMainIndex), at: 0)
  if let index = tokens.firstIndex(where: { $0.id == id }) {
      mainTokenIndex = index
			//	tokens.insert(tokens.remove(at: lastMainIndex), at: 0)
  }
}

mainIndex가 결정되고 나서 insert로 순서가 변경하기 때문에 발생한 버그였다.

func updateMainTokenIndex(id: UUID) {
    tokens.insert(tokens.remove(at: mainTokenIndex), at: 0)
    if let index = tokens.firstIndex(where: { $0.id == id }) {
        mainTokenIndex = index
    }
}

다중 선택

  • 흐름

선택 → 메인뷰 트리거 → 선택모드 스테이트 → 셀 형태 업데이트, 버튼 활성화 → 버튼 터치 → 메인뷰 상태 배열에 추가(아마 토큰을 추가해야할듯),(이 값으로 몇개 선택중인지 파악가능, 바로 업댓도 가능) → 삭제 누름 → 메인뷰의 트리거 → 상태 배열에 있는 모든 토큰을 서비스에게 요청해서 삭제 → 체크모드 false로 설정 → 모든 셀 다시 업데이트

  • 메인뷰에서 Token Cell View 터치 이벤트 관리

원래는 moveToken 액션을 발생시켰는데, checkBoxMode일 때에는 선택 상태값을 변경하는 trigger를 실행하도록 한다.

ForEach(viewModel.state.filteredTokens) { token in
  Button(action: {
      if viewModel.state.checkBoxMode {
          // 셀은 자기가 선택 상태인지 알 수 있는 상태 값이 필요하다
          
          
      } else {
          withAnimation(.spring(response: 0.5)) {
              viewModel.trigger(.moveToken(token.id))
              hideKeyboard()
          }
      }
      
  }, label: {
      TokenCellView(
          service: viewModel.state.service,
          token: token,
          isMain: false
      )
  })
  .matchedGeometryEffect(id: token.id, in: namespace, isSource: false)

꼭 에딧 버튼에 트리거를 연결할 필요가 없다. 메인뷰에서 관리하는 값이니까!

다만 셀이 선택되었을 떄 선택된 것을 표시하기 위해 선택되었는지 아닌지 알 수 있는 상태 값만 있으면 된다.

→ 뷰 생성시 넣어 줄 값

var checkList: [UUID:Bool] 로 선택한 값


겟 온리 스테이트로 생기는 문제

  • 다른 뷰에 뷰모델이 가진 상태값을 넘겨줄 수가 없다. get-only는 바인딩이 안됨.

  • isPresent 변수를 받아 화면 전환이나 alert를 하는 경우 get-only값을 넘겨주려고 하면 컴파일 에러가 발생함.

  • 그래서 프로토콜 수정함

    // 프로토콜
    var state: State { get set }
    
    // 구체타입
    var state: State {
      get {
          wrappedState()
      }
      set {
          _ = newValue
      }
    }
  • 원래는 그냥 wrappedState() 만 get으로 접근. 근데 setter도 정의해주어서 protocol 상으로는 set이 정의 되어 있어서 넘겨줄 수 있지만, set을 해도, 저렇게 들어온 값을 버리기 때문에 값을 변화시키지는 못함


삭제후 메인 화면 업데이트 할 때 메인 셀 인덱스가 tokens array의 인덱스를 초과하는 현상

  • 삭제 전 메인 인덱스를 다시 설정해주어야 하는 데 그러지 않아서 생긴 문제
  • 삭제 전에 메인 인덱스에 해당하는 토큰의 아이디를 기억하고 있다가, 삭제후에 tokens에서 해당 토큰의 아이디를 가진 토큰의 배열에서의 인덱스를 메인 인덱스에 할당한다.

선택 개수가 늦게 업데이트 된다..!

다중 선택 모드에서 선택을 하면 삭제할 개수가 표시되도록 했다. 그런데 선택을 해도 선택 개수가 업데이트가 바로 되지 않는 현상을 겪었다. 최종 상태값은 선택을 한 것으로 표시가 되는데, 선택 개수 텍스트뷰를 업데이트할 때에는 선택한 것이 반영되지 않았다.

그런데 단순히 코드의 순서를 바꾸었을 뿐인데 잘 동작하게 되었다.

// 이전 코드 
//state.selectedTokens[id] = false
//state.selectedCount -= 1

// 변경 코드
state.selectedCount -= 1
state.selectedTokens[id] = false

c

개인 회고

(솔직히 쓰기 - 현재 파트너 또는 누군가가 본다고 생각하지 말고 미래의 내가 본다고 생각하며 쓰면 어떨까요??😏)

어진

  • TokenEditView의 UI를 저번주에 잡아놨다고 생각했는데... 다른 크기의 디바이스에 올려보니 마음대로 spacing이 조절되지 않는다는것을 발견했다. 이를 디바이스마다 대응하는 방법을 찾게 되어서 하루종일 UI만 했다.ㅠㅠ
  • 이렇게 중복되는 일을 줄이기 위해 한 번 하더라도 확실하게 해놔야 겠다고 생각했다.

재명

  • 스터디 카페에서 팀원들과 함께 모여 작업했다. 다들 각자 할 일에 집중하느라 이야기할 시간이 거의 없었지만, 옆에 누군가 함께 하고 있다는 게 힘이 되었다. 중간에 편의점가서 잠시나마 커피 마시면서 이야기를 나눴는데 뭔가 회사생활하는 느낌이었다.
  • 이제 스위프트 유아이로 화면전환하는 법을 좀 이해했다. 큐알 코드 찍고 편집화면으로 넘어가는 이 작업 때문에 고생했는데 드디어 해결!
  • 삭제를 구현하던 중에 이것 저것 많은 문제 직면했다. 내일 어진님과 상의를 좀 해봐야겠다.
Clone this wiki locally