How to retrieve widget sizes in app to make a preview of it depending on the device?

6.4k views Asked by At

I am currently making a widget only application based on SwiftUI that allow users to show some type of informations as time, date etc.. In order to make my application functional I would like to make a preview of the widgets in the app view, also allowing users to set it up.

For now everything is working well, I can select a widget in my application and set it up so the widget updates as well. However, I am facing a problem, after some researches I can’t seem to find a way that allows you to get the widgets sizes according to the device you are using.

I tried to use UIScreen.main.bounds.size but the aspect ratio between widgets sizes and devices sizes aren’t the same.

What I would like to do is making my widget preview adapt to the widgets sizes that the device is actually using. (in other words retrieve the widgets sizes depending on which device you are on)

Is there any solutions to do it dynamically or do I need to make a view for each devices? Thanks in advance.

4

There are 4 answers

2
Viktor Gavrilov On BEST ANSWER

You could map UIScreen.main.bounds.size to widget sizes from Apple documentation: sizes for widgets https://developer.apple.com/design/human-interface-guidelines/widgets/overview/design/

Example for a Small widget:

switch UIScreen.main.bounds.size {
    case CGSize(width: 428, height: 926):   return 170
    case CGSize(width: 414, height: 896):   return 169
    case CGSize(width: 414, height: 736):   return 159
    case CGSize(width: 390, height: 844):   return 158
    case CGSize(width: 375, height: 812):   return 155
    case CGSize(width: 375, height: 667):   return 148
    case CGSize(width: 360, height: 780):   return 155
    case CGSize(width: 320, height: 568):   return 141
    default:                                return 155
    }
1
Tikhonov Aleksandr On

You can use displaySize of TimelineProviderContext to get size of current Widget.

func getTimeline(in context: Context, completion: @escaping (Timeline<Item>) -> Void) {
    print(context.displaySize)
    ...
}

You can find it in your Widget Provider.

1
Asi Givati On

i added more sizes if you want.

to get the family value you can use: @SwiftUI.Environment(\.widgetFamily) var family from inside your View

    func widgetHeight(forFamily family:WidgetFamily) -> CGSize {
    // better to use getTimeline func context.displaySize before this one.
    
    switch family {
    case .systemSmall:
        switch UIScreen.main.bounds.size {
        case CGSize(width: 428, height: 926):   return CGSize(width:170, height: 170)
        case CGSize(width: 414, height: 896):   return CGSize(width:169, height: 169)
        case CGSize(width: 414, height: 736):   return CGSize(width:159, height: 159)
        case CGSize(width: 390, height: 844):   return CGSize(width:158, height: 158)
        case CGSize(width: 375, height: 812):   return CGSize(width:155, height: 155)
        case CGSize(width: 375, height: 667):   return CGSize(width:148, height: 148)
        case CGSize(width: 360, height: 780):   return CGSize(width:155, height: 155)
        case CGSize(width: 320, height: 568):   return CGSize(width:141, height: 141)
        default:                                return CGSize(width:155, height: 155)
        }
    case .systemMedium:
        switch UIScreen.main.bounds.size {
        case CGSize(width: 428, height: 926):   return CGSize(width:364, height: 170)
        case CGSize(width: 414, height: 896):   return CGSize(width:360, height: 169)
        case CGSize(width: 414, height: 736):   return CGSize(width:348, height: 159)
        case CGSize(width: 390, height: 844):   return CGSize(width:338, height: 158)
        case CGSize(width: 375, height: 812):   return CGSize(width:329, height: 155)
        case CGSize(width: 375, height: 667):   return CGSize(width:321, height: 148)
        case CGSize(width: 360, height: 780):   return CGSize(width:329, height: 155)
        case CGSize(width: 320, height: 568):   return CGSize(width:292, height: 141)
        default:                                return CGSize(width:329, height: 155)
        }
    case .systemLarge:
        switch UIScreen.main.bounds.size {
        case CGSize(width: 428, height: 926):   return CGSize(width:364, height: 382)
        case CGSize(width: 414, height: 896):   return CGSize(width:360, height: 379)
        case CGSize(width: 414, height: 736):   return CGSize(width:348, height: 357)
        case CGSize(width: 390, height: 844):   return CGSize(width:338, height: 354)
        case CGSize(width: 375, height: 812):   return CGSize(width:329, height: 345)
        case CGSize(width: 375, height: 667):   return CGSize(width:321, height: 324)
        case CGSize(width: 360, height: 780):   return CGSize(width:329, height: 345)
        case CGSize(width: 320, height: 568):   return CGSize(width:292, height: 311)
        default:                                return CGSize(width:329, height: 345)
        }
        
    default:                                return CGSize(width:329, height: 345)
    }
}
2
Stu P. On

If you're using swift UI you can use a Geometry Reader or use the Environment property wrapper like so and you can compare it against the three sizes .systemSmall, Medium, Large

@Environment(\.widgetFamily) var widgetFamily