Mobile
layout with traffic light on iPad
Quang Dev.to (EN Zone)
28 views
After fixing these 2 issues for Focus browser & Firefox iOS, I decided to ask Codex to make me a small demo for these 2 layout guide:
blue = safe area layout (safeAreaLayoutGuide)
orange = layoutGuide(for: .margins(cornerAdaptation: .horizontal)) (introduced in iOS 26 SDKs)
What if we use layoutGuide(for: .margins(cornerAdaptation: .vertical))? It goes right underneath the traffic light.
As we can see, safe area layout can help with the notch or home bar but won't help us avoid UI components getting overlapped with iPadOS traffic light in window mode. .margins(cornerAdaptation: on the other hand, is introduced for that scenario.
Source code for the small demo:
import SwiftUI
struct BlogWindowControlLayoutSimplifiedDemo: View {
@State private var safeAreaFrame = CGRect.zero
@State private var windowControlFrame = CGRect.zero
var body: some View {
GeometryReader { proxy in
ZStack(alignment: .topLeading) {
LayoutRegionOutline(
frame: safeAreaFrame,
color: .blue,
dash: []
)
LayoutRegionOutline(
frame: windowControlFrame,
color: .orange,
dash: [8, 5]
)
WindowLayoutGuideReader(
safeAreaFrame: $safeAreaFrame,
windowControlFrame: $windowControlFrame
)
.allowsHitTesting(false)
}
.frame(width: proxy.size.width, height: proxy.size.height)
}
.ignoresSafeArea()
.overlay(alignment: .bottomLeading) {
LayoutLegend()
.padding(16)
}
}
}
private struct LayoutRegionOutline: View {
let frame: CGRect
let color: Color
let dash: [CGFloat]
var body: some View {
Rectangle()
.stroke(color, style: StrokeStyle(lineWidth: 2, dash: dash))
.frame(width: max(0, frame.width), height: max(0, frame.height))
.position(x: frame.midX, y: frame.midY)
.allowsHitTesting(false)
}
}
private struct LayoutLegend: View {
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Label("safeAreaLayoutGuide", systemImage: "rectangle.dashed")
.foregroundStyle(.blue)
Label("window-control margins", systemImage: "rectangle.dashed")
.foregroundStyle(.orange)
}
.font(.caption)
.padding(10)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 10))
}
}
private struct WindowLayoutGuideReader: UIViewRepresentable {
@Binding var safeAreaFrame: CGRect
@Binding var windowControlFrame: CGRect
func makeCoordinator() -> Coordinator {
Coordinator(
safeAreaFrame: $safeAreaFrame,
windowControlFrame: $windowControlFrame
)
}
func makeUIView(context: Context) -> LayoutProbeView {
let view = LayoutProbeView()
view.onFramesChanged = { [weak coordinator = context.coordinator] safeArea, windowControl in
coordinator?.report(safeAreaFrame: safeArea, windowControlFrame: windowControl)
}
return view
}
func updateUIView(_: LayoutProbeView, context _: Context) {}
final class Coordinator {
private var safeAreaFrame: Binding<CGRect>
private var windowControlFrame: Binding<CGRect>
private var pendingFrames: (safeArea: CGRect, windowControl: CGRect)?
private var updateScheduled = false
init(safeAreaFrame: Binding<CGRect>, windowControlFrame: Binding<CGRect>) {
self.safeAreaFrame = safeAreaFrame
self.windowControlFrame = windowControlFrame
}
func report(safeAreaFrame: CGRect, windowControlFrame: CGRect) {
pendingFrames = (safeAreaFrame, windowControlFrame)
guard !updateScheduled else { return }
updateScheduled = true
Task { @MainActor [weak self] in
guard let self else { return }
updateScheduled = false
guard let frames = pendingFrames else { return }
pendingFrames = nil
if self.safeAreaFrame.wrappedValue != frames.safeArea {
self.safeAreaFrame.wrappedValue = frames.safeArea
}
if self.windowControlFrame.wrappedValue != frames.windowControl {
self.windowControlFrame.wrappedValue = frames.windowControl
}
}
}
}
}
private final class LayoutProbeView: UIView {
var onFramesChanged: ((CGRect, CGRect) -> Void)?
override func layoutSubviews() {
super.layoutSubviews()
let safeAreaFrame = safeAreaLayoutGuide.layoutFrame
if #available(iOS 26.0, *) {
let windowControlLayoutGuide = layoutGuide(for: .margins(cornerAdaptation: .vertical))
onFramesChanged?(safeAreaFrame, windowControlLayoutGuide.layoutFrame)
} else {
onFramesChanged?(safeAreaFrame, safeAreaFrame)
}
}
}
#Preview {
BlogWindowControlLayoutSimplifiedDemo()
}
Read original: https://dev.to/quangdecember/layout-with-traffic-light-on-ipad-13m1
← Previous
I rented three servers, told nobody, and watched the internet break in anyway
Next →
A quick review of SQL Joins
Related
How Minecraft Java Edition Actually Runs on Android (JVM, Vulkan, and the Zink Translation Layer)
Mobile
25
DEV Community
Why I built an ad-free Android video player to replace bloated alternatives
Mobile
30
DEV Community
Parsing bank statements in memory: keep the fields, store nothing else
Mobile
34
DEV Community
Getting started with iOS VoiceOver: a beginner's guide
Mobile
28
Dev.to (EN Zone)
Comments0
No comments yet — be the first