Concurrent (동시성) 이란 무엇인가?

  • 앱의 로직 내에서 특정 부분이 동시에 또는 임의 순서대로 실행되기 위해 필요한 개념
  • 앱의 전반적인 효율성 측면에서도 필요하지만, 앱을 사용하는 유저에게도 일관적인 UI를 그리기 위해서도 필요한 개념
  • ⇒ 네트워크 통신을 통해 어떠한 리소스를 다운받는 동안 동시성을 적용하지 않아 유저가 비어있는 화면만 바라보게 된다면 지루함을 느끼고, 짜증을 유발할 수 있다, 이때 동시성을 적용하여 네트워킹을 통해 리소스를 다운받는 동안 유저에게 특정 뷰를 그려주면 유저 입장에서도 매우 좋다
  • 데이터 흐름을 정확히 유지하는 것이 중요하기 때문에 동시에 이루어지는 두 작업이 하나의 데이터를 조작하면 안됨

GCD (Grand Central Dispatch)

  • DispatchQueue 에 등록된 작업들을 사용 가능한 스레드에 스케쥴링 API
  • Serial DispatchQueue 와 Concurrent DispatchQueue 로 나뉨
  • Serial queue ⇒ queue의 작업을 오직 하나의 스레드에서만 수행
  • Concurrent queue ⇒ 여러 스레드의 작업을 동시에 수행

동기와 비동기

  • Sync 와 Async 로 동기 및 비동기를 설정할 수 있다.
  • 동기적으로 설정된 작업은 해당 작업이 반환(종료)되기 전까지 다른 작업을 수행하지 않는다
  • 비동기적으로 설정된 작업은 해당 작업이 수행되는 동안 다른 작업을 동시에 수행하며, 작업이 종료되는 즉시 반환한다
  • 의문점.. → Serial queue / Sync 와 Concurrent queue / Async 의 차이가 뭐지?...

 https://cskime.tistory.com/18

Seiral/Concurrent는 요청된 여러 개의 작업들을 순차적으로 또는 동시에 처리 하는 경우
Sync/Async는 어떤 작업에 대한 요청과 응답이 한 번에 또는 따로 발생 하는 경우
Serial/Concurrent는 '다수의 대상에 대한 '순서'가 주된 개념
Sync/Async는 단일 작업에 대한 요청과 응답(결과)의 발생 시점이 주된 개념

 

DispatchQueue 의 종류

Main Queue

  • 앱의 시작과 동시에 생성되며 Serial Queue로 존재한다 메인큐는 반드시 UI를 위해서만 사용해야 하며 언제나 비동기적으로 작업을 수행한다
  • DispatchQueue.main.async { task }

Global Queue

  • Concurrent Queue 로 존재
  • QoS 파라미터를 가지며, 해당 파라미터에 따라 중요 순서도가 결정됨
    • QoS 종류
      • userInteractive : 사용자화 직접 상호작용 하는것들 ( UI 업데이트 , 애니메이션 등등 )
      • userInitated : 이벤트 발생 시 즉각적인 결과가 필요한 작업
      • Default : 일반적인 작업 ( global() 은 QoS 파라미터를 생략할수 있기 때문에 굳이 디폴트를 사용하진 않음 )
      • utility : progress bar 와 같이 길~게 싱행되는 작업들 ( 데이터 다운로드 등등 )
      • background : 유저가 인지할 필요가 없는 시간과 관계없는 작업들 ( 동기화 , 백업 등,, 악용하면 이걸로 정보탈취..? )
      • unspecified : 거의 사용되지 않는 파라미터로, QoS 정보가 없을때 사용
  • DispatchQueue.global(qos: 1~6의 qos 또는 생략 가능).sync/async { task }

Custom Queue

  1. 커스텀으로 큐를 구성할 때 사용
  2. 기본적으로 Serial 특성을 갖긴 하지만, 설정을 통해 Concurrent 로 사용 가능
// serial 커스텀 큐 ( 기본값 )
let customSerialQueue = DispatchQueue(label: "customSerial")

// concurrent 커스텀 큐 ( 애트리뷰트 설정을 해야함 ) , qos 알아서 추론
let customConcurrentQueue = DispatchQueue(label: "customConcurrent", attributes: .concurrent)

// concurrent 커스텀 큐에 qos 설정
let customConcurrentQueue = DispatchQueue(label: "customConcurrent", qos: .background, attributes: .concurrent)

 

참고 링크

https://hyunsikwon.github.io/ios/iOS-Concurrency-01/#gcd

https://engineering.linecorp.com/ko/blog/about-swift-concurrency/

https://sujinnaljin.medium.com/ios-차근차근-시작하는-gcd-5-c8e6eee3327b

패스트 캠퍼스 All in one_iOS 강의

DTO 와 VO 의 차이

API를 호출하고자 하는데 데이터 정의를 어떻게 해야 할지 고민이 되어 찾아보다가 관련 개념을 정리

우선, DTO 와 VO 둘다, 데이터를 정의하는데 의미가 있다 그렇다면 차이가 뭘까?

간단히 설명하면 getter 와 setter를 모두 포함하는지, getter만 포함하는지에 대한 차이가 있다.

즉, 읽기 전용 데이터인지? 적절한 수정을 통해 가공해야 하는 데이터인지에 대한 차이이다.

DTO : Data Transfer Object

⇒ getter 와 setter를 모두 가지고 있는 객체를 의미

Swift의 문법중 ‘연산 프로퍼티' 의 개념과 동일하다, 데이터를 가져와 적절히 가공하고 사용해야 할때 사용되는 개념

  • 데이터 자체를 사용하는것이 아닌, 가공 또는 한곳에 모아 사용해야 할때 사용
  • 데이터를 어딘가에 ( 외부로 ) 전송해야 할때 유용한 객체
class Greeting {
    private var korean = "안녕하세요"
    private var english = "Hello"
    
    func getKorean() -> String {
        return korean
    }
    
    func setKorean(_ korean: String) {
        self.korean = korean
    }
    
    func getEnglish() -> String {
        return english
    }
    
    func setEnglish(_ english: String) {
        self.english = english
    }
}

VO : Value Object

  • 데이터 그 자체로 의미 있는 값을 가지는것
  • DTO와 달리, getter만 가질수 있으므로 읽기 전용 객체가 된다
open class UIColor : NSObject, NSSecureCoding, NSCopying {

    //...
    open class var black: UIColor { get } // 0.0 white
    open class var darkGray: UIColor { get } // 0.333 white
    open class var lightGray: UIColor { get } // 0.667 white
    open class var white: UIColor { get } // 1.0 white
    open class var gray: UIColor { get } // 0.5 white
    open class var red: UIColor { get } // 1.0, 0.0, 0.0 RGB
    open class var green: UIColor { get } // 0.0, 1.0, 0.0 RGB
    open class var blue: UIColor { get } // 0.0, 0.0, 1.0 RGB
    open class var cyan: UIColor { get } // 0.0, 1.0, 1.0 RGB
    open class var yellow: UIColor { get } // 1.0, 1.0, 0.0 RGB
    open class var magenta: UIColor { get } // 1.0, 0.0, 1.0 RGB
    open class var orange: UIColor { get } // 1.0, 0.5, 0.0 RGB
    open class var purple: UIColor { get } // 0.5, 0.0, 0.5 RGB
    open class var brown: UIColor { get } // 0.6, 0.4, 0.2 RGB
    open class var clear: UIColor { get } // 0.0 white, 0.0 alpha
    //...
}
  • 대표적으로 UIColor 가 VO 이다.
  • color 를 사용할때 .green 을 사용하면, green 색상을 바로 읽어 올수 있다.

참고 : 링크

개요 :

A control that displays one or more buttons in a tab bar for selecting between different subtasks, views, or modes in an app.

→ 앱의 각자 다른 뷰 또는 모드 중 하나를 선택하기 위해 탭 표시줄에 하나 이상의 버튼을 표시하는 컨트롤.

정의

@MainActor class UITabBar : UIView

→ UIView를 상속받는 클래스로 정의되어 있다

StoryBoard 사용하여 TabBar 구성하기

  1. TabBar 로 표현할 View를 선택한후, 상단의 Editor - Embed In - Tab Bar Controller 를 선택한다

2. TabBar 구성 확인 : TabBar가 연결됨을 확인한다 ( segue 에 대해서 더 깊은 공부 필요 )

3. 연결된 화면에서 Tab을 선택하여, 인스펙터 창에서 Tab Item을 수정한다 ( Title 에는 Tab Item의 이름을, Image 에서 Tab Item의 기호를 수정한다 → HIG를 참고하여 SF Symbol 활용 )

4. [ Shift + Command + L ] 단축키를 이용해 컴포넌트 창을 열어 새로 추가할 ViewController를 추가

5. Tab Bar Controller 를 선택한후, control 키를 누른 상태로 방금 생성한 뷰 컨트롤러랑 연결한다

→ 이때, Relationship Segue view controllers 를 선택하면 탭바와 연결됨을 확인할 수 있다

6. 새로 생성한 ViewController를 UIViewController 를 상속받는 ViewController class와 연결

→ 인스펙터 창에서 class를 선택하여 지정한 ViewController와 연결한다

  • 위 순서대로 설정하고 앱을 실행하면 TabBar가 정상적으로 적용됨을 확인 할 수 있다!

 

⇒ 여기 까지, Storyboard 를 사용하여 TabBar를 적용하는 방법을 알아 봤다

CodeBase 로도 구현하는 방법이 있다, 이 방법을 쓰면 스토리보드를 사용하지 않고 오직 코드로만 구성할 수 있지만 아직은 공부하는 단계이므로 스토리보드 기반으로 적용을 한후, 시간이 남으면 프로젝트 자체를 모두 코드로만 구현해보자!

TabBar 란 무엇인가?

  • 화면 하단에 탭 표시줄이 나타나 사람들이 앱이 제공하는 정보 유형이나 기능을 이해할 수 있도록 도움
  • 각 섹션 내의 현재 내비게이션 상태를 유지하면서 앱의 최상위 섹션 사이를 빠르게 전환
  • 기본적으로 탭 표시줄은 반투명이며, 콘텐츠가 뒤에 나타날 때만 배경 자료를 사용
  • 키보드가 화면에 있을 때 탭 표시줄이 숨김

  • 사진 앱과 같이 탭바 뒤에 컨텐츠가 있으면 배경색을 보여주고, 하단으로 스크롤 하면 뒤에 비치지 않음
  • 장치 크기와 방향에 따라, 보이는 탭의 수는 총 탭 수보다 적을 수도 있다
  • 화면에 모든 탭이 표시되지 않는경우, 더보기 탭이 되어 목록에 나머지 항목을 별도의 화면으로 표시

공식 문서 : UITabBar

Tip 
TabBar 와 ToolBar는 모두 화면 하단에 나타나지만, 서로 다른 목적을 갖는다 탭 바는 한 화면에서 탭에따라 다른 뷰를 표시해 주지만, ToolBar의 경우 항목 생성, 필터링, 콘텐츠 표시등을 위한 버튼 등이 포함된다 TabBar와 ToolBar는 동시에 표시할 수 없다.

지켜야 할점

  • 어떠한 기능을 수행하기 위해 사용하지 말고, 오직 탭을 전환하는 용도로만 사용할것
  • View의 요소에 어떠한 기능을 제공해야 하는 경우 Toolbar를 사용할것.
  • 적당한 수의 탭을 사용할것, iPhone의 경우 3~5개 , iPad의 경우 조금더 많이 사용할것

⇒ 너무 많거나, 너무 적으면 오히려 디자인적 요소가 깨짐

  • iPadOS 앱에서는 TabBar대신 SideBar를 사용하도록 권장
  • Modal View를 제외한 다른 View에서 언제 어디서나 유저가 바로 이동할수 있도록 TabBar를 숨기지 말것
  • 해당 탭의 컨텐츠를 이용할수 없는 경우에도 Tab을 숨기지 말고 이용할 수 없는 설명을 보여줄것
  • 현재 선택된 탭에서 다른 탭의 View에 영향을 끼치지 말아야 함

→ 예를 들어, 분할된 view의 왼쪽에 부분에 있는 tab을 선택했다면, 분할된 view의 오른쪽에 갑자기 변경되지 않아야 하고 Popover에서 tab을 선택했다면 popup 뒤의 뷰가 변경되지 않아야 한다

  • 소통을 위해서 badge를 사용할것 ( 알림이 있다면 뱃지를 사용하여 표시 ) ex) 카카오톡 채팅 탭

→ 공식 문서 : UITabBarItem

  • SF 기호를 사용하여 확장 가능하고 시각적으로 일관된 TabBar 항목을 제공하는 것을 고려할 것

  • Custom Tab Bar glyphs를 만들어야 하는 경우, 탭 바가 어떠한 환경에서든 일관성있게 보이도록 글리프를 두 가지 크기로 만들것   ( Regular, Compact )

 Glyphs 문서

 

 

본 내용은 스윗한 SwiftUI 책에 있는 예제를 공부한 내용입니다.
해당 예제들에 대한 저작권은 BJpublic 에 있습니다.

 

  • 이미지에 frame 속성을 사용해도 이미지 자체의 크기를 변경해 주진 않음
  • Resizable 속성을 사용하여 이미지 크기를 변경할 수 있음
HStack {
	Image("imgFile")     // 이미지 크기는 변하지 않고, 이미지를 포함함 뷰의 크기가 변함
		.frame(width: 50, height: 50)

	Image("imgFile").resizable()    // resizable() 을 사용하여 이미지 크기 자체가 변함
		.frame(width: 200, height: 50)
}

  • frame 은 View의 속성이기 때문에 resizable 과 순서가 중요함!
  • resizable() 을 사용하여 특정 영역만 늘려주는 것도 가능함
  • 단, UIKit 처럼 ResizingMode 를 생략하면 tile 이 아니라 stretch가 기본값이 됨
HStack {
            Image("swift")
            //capInset 매개 변수에 늘어날 영역 지정. resizingMode 생략 시 stretch 적용
                .resizable(capInsets: .init(top: 0, leading: 50, bottom: 0, trailing: 0))
                .frame(width: 150, height: 150)
            
            Image("swift")
                .resizable(resizingMode: .tile)
                .frame(width: 150, height: 150)
        }

 

ContentMode

UIKIt SwiftUI 설명
Scale to Fill Default 값 종횡비와 상관없이 이미지를 늘려 표현
Aspect Fit .scaledToFit() 종횡비를 유지한 채로 표현가능한 최대로 표현
Aspect Fill .scaledToFill() 종횡비를 유지한 채로 표현하며, 이미지가 짤리거나 추가적으로 더 크게 표현

 

HStack(spacing: 30){
						// 기본값
            Image("swift").resizable().frame(width: 100, height: 150)
            
						// UIKit의 Aspect Fit 적용
            Image("swift").resizable()
                .scaledToFit()
                .frame(width: 100, height: 150)

						// UIKit의 Aspect Fill 적용            
            Image("swift").resizable()
                .scaledToFill()
                .frame(width: 100, height: 150)
        }

 

AspectRatio

  • 이미지 비율을 좀더 세부적으로 조정하기 위해 사용하는 수식어
  • 모든 콘텐츠가 적용된 상황에서 추가적으로 비율조정을 하기위해 사용
// CGFloat : 너비 / 높이 를 계산한 비율을 전달
func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View

// CGSize : 너비 와 높이를 각각 설정
func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View

AspectRatio 적용한 예제

HStack(spacing: 30) {
            //scaledToFit 콘텐츠 모드를 적용한 뒤, 너비가 높이보다 1.6배 비율을 가지도록 조정
            Image("swift").resizable()
                .aspectRatio(CGSize(width: 1.6, height: 1), contentMode: .fit)
                .frame(width: 150, height: 150)
            
            // scaledToFill 콘텐츠 모드를 적용한뒤, 너비가 높이보다 0.7배의 비율을 가지도록 조정
            Image("swift").resizable()
                .aspectRatio(0.7, contentMode: .fill)
                .frame(width: 150, height: 150)
                .clipped()
        }

 

ClipShape

  • 이미지를 원하는 모양으로 조정
  • 크기 또한 조정 가능
HStack(spacing: 30) {
            // 원 모양
            Image("swift").clipShape(Circle())
            
            // 이미지 크기보다 사방으로 10씩 크기를 조정한 사각형
            Image("swift").clipShape(Rectangle().inset(by: 10))
            
            // 크기와 위치를 직접 지정한 타원
            Image("swift").clipShape(Ellipse().path(in: CGRect(x: 10, y: 10, width: 80, height: 110)))
        }

 

RanderingMode

  • template : 이미지의 불투명 영역이 가진 본래의 색을 무시하고 원하는 색으로 변경
  • original : 항상 이미지의 본래 색을 유지
  • 렌더링 모드를 생략하면 시스템이 알아서 조정을 하므로, 원치않은 결과가 나오면 렌더링 모드를 지정해야함
HStack(spacing: 30) {
            // 렌더링 모드 생략 -> 시스템이 스스로 결정
            Image("swift")
            
            // 원본 이미지 색상 유지
            Image("swift").renderingMode(.original)
            
            // template 모드 적용
            Image("swift").renderingMode(.template)
        }
        .foregroundColor(.blue) // 자식 뷰 모두에게 일괄 적용

 

SF Symbol

  • 애플에서 직접 만들고 제공하는 이미지들의 모음
  • iOS 13이상부터 사용 가능
  • 벡터 기반의 이미지로, 시스템 폰트로 크기를 조정 할 수도 있음
  • 앱에서 심볼들을 확인 할 수 있으며, systemName: 으로 간단히 사용 가능
HStack(spacing: 30){
            Image(systemName: "star.circle")
            Image(systemName: "star.circle.fill")
        }

  • ImageScale 을 적용하면 크기를 지정 할 수 있다. ( 기본값 → medium )
HStack(spacing: 30) {
            Image(systemName: "star.circle").imageScale(.small)
            Image(systemName: "star.circle")
            Image(systemName: "star.circle").imageScale(.large)
        }

  • font 속성을 이용해서 크기를 조정 할 수도 있다
HStack(spacing: 30) {
            Image(systemName: "star.circle").font(.body)
            Image(systemName: "star.circle").font(.title)
            Image(systemName: "star.circle").font(.system(size: 40))
            Image(systemName: "star.circle").imageScale(.large).font(.system(size: 40))
        }

  • weight 속성을 이용하면 굵기 표현도 가능 하다
HStack(spacing: 30) {
            Image(systemName: "star.circle").font(Font.title.weight(.black))
            Image(systemName: "star.circle").font(Font.title.weight(.semibold))
            Image(systemName: "star.circle").font(Font.title.weight(.light))
            Image(systemName: "star.circle").font(Font.title.weight(.ultraLight))
        }

 

+ Recent posts