I’m developing an iOS widget and encountered an issue where the layout behaves differently between systemSmall
and systemMedium
sizes.
As shown in the attached image, the top and bottom elements that are visible in the systemSmall
widget do not appear in the systemMedium
version. However, the code is the almost same for both sizes, and I can’t find any layout-related conditions that would affect this behavior.
Here’s some context on the widget structure:
- The root layout uses a ZStack to place a background behind the content.
- The content is built with a VStack, containing three sections: top, middle, and bottom.
- I use Spacer() between each section to distribute them vertically.
- There doesn’t seem to be any conditional logic affecting the layout based on the widget size.
Here is the relevant code snippet:
import Foundation
import SwiftUI
import WidgetKit
struct TodayQuestionProvider: TimelineProvider {
func placeholder(in context: Context) -> TodayQuestionEntry {
TodayQuestionEntry(date: Date(), question: "Today Question")
}
func getSnapshot(in context: Context, completion: @escaping (TodayQuestionEntry) -> Void) {
let entry: TodayQuestionEntry
if context.isPreview {
entry = placeholder(in: context)
} else {
let question = "Today Question"
entry = TodayQuestionEntry(date: Date(), question: question)
}
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
getSnapshot(in: context) { (entry) in
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
struct TodayQuestionEntry: TimelineEntry {
let date: Date
let question: String
}
struct TodayQuestionWidgetEntryView: View {
var entry: TodayQuestionProvider.Entry
@Environment(\.colorScheme) var colorScheme
@Environment(\.widgetFamily) var family
private func formattedDate() -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "EEEE"
return formatter.string(from: entry.date)
}
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.clear)
.background(
colorScheme == .dark
? LinearGradient(
stops: [
Gradient.Stop(color: Color(red: 0.27, green: 0.27, blue: 0.27), location: 0.00),
Gradient.Stop(color: .black, location: 0.67),
],
startPoint: UnitPoint(x: 0.5, y: 0),
endPoint: UnitPoint(x: 0.5, y: 1)
)
: LinearGradient(
stops: [
Gradient.Stop(color: .white, location: 0.20),
Gradient.Stop(color: Color(red: 0.91, green: 0.91, blue: 0.91), location: 1.00),
],
startPoint: UnitPoint(x: 0.5, y: 0),
endPoint: UnitPoint(x: 0.5, y: 1)
)
)
.scaledToFill()
VStack(spacing: 0) {
HStack {
Text(formattedDate())
.font(Font.custom("Pretendard", size: 13).weight(.medium))
.foregroundColor(Color(red: 0.6, green: 0.6, blue: 0.6))
.padding(.leading, 16)
Spacer()
}
.padding(.top, 12)
.frame(maxHeight: family == .systemMedium ? 24 : nil)
Spacer(minLength: 2)
// ISSUE: Shown only this text when systemMedium
Text(entry.question)
.font(
Font.custom(
"Pretendard",
size: family == .systemSmall ? 16 : 20
).weight(family == .systemSmall ? .semibold : .bold)
)
.foregroundColor(
colorScheme == .dark
? Color(red: 0.73, green: 0.73, blue: 0.73)
: .black
)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 16)
.frame(maxHeight: family == .systemMedium ? 79 : nil)
Spacer(minLength: 2)
HStack(spacing: 7) {
ZStack {
RoundedRectangle(cornerRadius: 30)
.fill(colorScheme == .dark ? .white : .black)
HStack(spacing: 2) {
Image("Plus")
.resizable()
.frame(width: 20, height: 20)
Text("Post")
.font(Font.custom("Pretendard", size: 14).weight(.semibold))
.foregroundColor(colorScheme == .dark ? .black : .white)
}
}
.frame(height: 32)
.frame(maxWidth: .infinity)
ZStack {
RoundedRectangle(cornerRadius: 30)
.fill(colorScheme == .dark ? .white : .black)
HStack(spacing: 2) {
Image("Refresh")
.resizable()
.frame(width: 20, height: 20)
if family == .systemMedium {
Text("Refresh")
.font(Font.custom("Pretendard", size: 14).weight(.semibold))
.foregroundColor(colorScheme == .dark ? .black : .white)
}
}
}
.frame(height: 32)
.frame(width: family == .systemSmall ? 32 : nil)
.frame(maxWidth: family == .systemSmall ? nil : .infinity)
}
.padding(.horizontal, 8)
.padding(.bottom, 7)
.frame(maxHeight: family == .systemMedium ? 32 : nil)
}
}
}
}
struct TodayQuestionWidget: Widget {
let kind: String = "TodayQuestionWidget"
var body: some WidgetConfiguration {
let configuration = StaticConfiguration(kind: kind, provider: TodayQuestionProvider()) {
entry in
if #available(iOS 17.0, *) {
TodayQuestionWidgetEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
TodayQuestionWidgetEntryView(entry: entry)
}
}
.configurationDisplayName("Today Question")
.description("Check today's question and write a log")
.supportedFamilies([.systemSmall, .systemMedium])
if #available(iOS 15.0, *) {
return configuration.contentMarginsDisabled()
}
return configuration
}
}
#Preview(as: .systemMedium) {
// #Preview(as: .systemSmall) {
TodayQuestionWidget()
} timeline: {
TodayQuestionEntry(date: .now, question: "Text on widget, text on widget, text on widget")
}
I’m wondering what might be causing this discrepancy in layout rendering.
Is there something about how Spacer behaves differently in systemSmall
vs. systemMedium
?
Any help would be appreciated!