![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/Ro9HU/btr2vo5dk7U/1hyls5Xi7zwzVyUXwHPkD0/img.png)
widget 작업을 위해 SwiftUI를 사용할 일이 생겼을 때 이슈 하나 있어서 정리 하나 해보겠습니다. 결론부터 말하자면 VStack과 HStack 모두 기본적으로 spacing이 들어가 있습니다. 코드로 예시를 들겠습니다. struct ContentView: View { var body: some View { VStack { Text("Hello, world!") Divider() Text("글자 spacing 테스트") } } } Text, Divider와 Text 사이에 기본적으로 spacing이 애플처럼 보기 좋게 들어가 있습니다. 하지만 작업을 하다보면 자유롭게 padding을 넣어서 간격을 만들일이 생기게 됩니다. 기존 코드에 padding을 넣으면 디자인 시안과 미묘하게 다르게 됩니다. ..
![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/svS9D/btr02znd1tn/FBiztKMFoNkMo4bxmYZUdk/img.png)
UILabel 을 사용하다 보면 글자가 길어짐에 따라 라인이 추가되는데 한정된 영역에서 글자를 어떻게 처리할 지 고민하게 된다. 그러면서 lineBreakMode가 나왔고 애플에서는 가독성을 위해서 글자를 단어 단위로 잘라서 끝쪽에 애매한 부분은 다음 줄 부터 시작하게 됩니다. 일반적인 상황에서는 상관이 없지만 네이버 시리즈의 제목을 표기하는 곳같이 글자를 단어 단위로 짜르게 되면 영역이 상당히 비어 보이는 느낌을 가지게 됩니다. 그래서 문자 단위로 자르기 위해 label.lineBreakMode = .byCharWrapping 으로 변경을 하게 됩니다. 아쉽게도 .byCharWrapping 는 글자가 짤리는 영역에 … 으로 축소해주고 싶지만.. 이런식으로 끝에 넘어가도 글자가 …으로 축소 되지않습니다...
![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/bMf2oI/btr07zUJn79/PMMUbM59Jv6mekj16ejiZ1/img.png)
NSTextAttachment 속성을 이용해서 UILabel 앞에 이미지 붙이기 import UIKit class ViewController: UIViewController { @IBOutlet weak var testLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // 테스트할 텍스트 생성 let text = "텍스트 앞에 이미지 넣기" // NSAttributedString 으로 변경 let NStext = NSAttributedString(string: text) // attributeString 생성 let attributedString = NSMutableAttributedString(string: "") // UIImage ..
마지막 위젯을 그리는 뷰에 대해 설명하겠습니다. struct widgetWidgetEntryView : View { var entry: Provider.Entry var body: some View { Text(entry.date, style: .time) } } 일반적인 SwiftUI 에서 보여지는 뷰 입니다. @Environment(\.widgetFamily) private var family 를 선언하고 바디에서 switch famliy 를 이용하면 case 에 따른 뷰를 그릴수있습니다. struct OrdersWidgetView: View { var entry: OrdersWidget.Entry @Environment(\.widgetFamily) private var family var body:..
위젯의 핵심인 TimelineProvider에 대해 알아보겠습니다. 처음 설치했을때 샘플로 되어있는 코드입니다. struct Provider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date()) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> (..
![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/NIhbp/btr0VJwRgSt/7dxwenBajMZSNWvyU1AJKk/img.png)
위젯 생성되는 파일중에 Widget에 들어가면 struct widgetWidget: Widget { let kind: String = "widgetWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in widgetWidgetEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") .supportedFamilies([.systemSmall,.systemMedium,.systemLarge,.accessoryCircular,.access..
![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/dZTP9l/btr0KGAWYdp/2z2kRMHEAmwckYIUpbPyw1/img.png)
번들 밑에 있는 파일에 들어가보겠습니다. import WidgetKit import SwiftUI struct Provider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date()) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { var..
![](http://i1.daumcdn.net/thumb/C148x148.fwebp.q85/?fname=https://blog.kakaocdn.net/dn/cgt75u/btr0N93XANn/tt7peTfLX4eQQJsrgZKwwK/img.png)
위젯을 target을 추가하면 자신이 추가한 확장자명으로 된 폴더가 하나 만들어지게 됩니다. 먼저 bundle로 가보겠습니다. import WidgetKit import SwiftUI @main struct widgetWidgetBundle: WidgetBundle { var body: some Widget { widgetWidget() } } 와 같은 형식으로 되어있고 번들에는 앞으로 만들 위젯에 대한 구조체를 선언해서 놓으면 됩니다. 애플에서 제공하는 FoodTruck 샘플을 보면 아래와 같이 사용하고 있습니다. import WidgetKit import SwiftUI @main struct Widgets: WidgetBundle { var body: some Widget { // MARK: - Li..