Created
February 14, 2020 20:05
-
-
Save mayoff/f617062b6dd9418cb19d3f361e2fa95d to your computer and use it in GitHub Desktop.
playground demonstrating SwiftUI PreferenceKey, EnvironmentKey, and GeometryReader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import PlaygroundSupport | |
struct EqualWidthKey: PreferenceKey { | |
static var defaultValue: CGFloat? { nil } | |
static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) { | |
switch (value, nextValue()) { | |
case (_, nil): break | |
case (nil, let next): value = next | |
case (let a?, let b?): value = max(a, b) | |
} | |
} | |
} | |
extension EqualWidthKey: EnvironmentKey { } | |
extension EnvironmentValues { | |
var equalWidth: CGFloat? { | |
get { self[EqualWidthKey.self] } | |
set { self[EqualWidthKey.self] = newValue } | |
} | |
} | |
struct EqualWidthModifier: ViewModifier { | |
var alignment: Alignment | |
@Environment(\.equalWidth) var equalWidth | |
func body(content: Content) -> some View { | |
return content | |
.background( | |
GeometryReader { proxy in | |
Color.clear | |
.preference(key: EqualWidthKey.self, value: proxy.size.width) | |
} | |
) | |
.frame(width: equalWidth, alignment: alignment) | |
} | |
} | |
extension View { | |
func equalWidth(alignment: Alignment) -> some View { | |
return self.modifier(EqualWidthModifier(alignment: alignment)) | |
} | |
} | |
struct EqualWidthHost: ViewModifier { | |
@State var width: CGFloat? = nil | |
func body(content: Content) -> some View { | |
return content | |
.environment(\.equalWidth, width) | |
.onPreferenceChange(EqualWidthKey.self) { self.width = $0 } | |
} | |
} | |
extension View { | |
func equalWidthHost() -> some View { | |
return self.modifier(EqualWidthHost()) | |
} | |
} | |
struct Message { | |
var sender: String | |
var body: String | |
} | |
struct MessageView: View { | |
var message: Message | |
var body: some View { | |
HStack(alignment: .bottom, spacing: 3) { | |
Text(message.sender + ":").padding(2) | |
.equalWidth(alignment: .trailing) // <-- THIS IS THE NEW MODIFIER | |
Text(message.body) | |
.fixedSize(horizontal: false, vertical: true).padding(6) | |
.background(Color.blue.opacity(0.2)) | |
} | |
} | |
} | |
struct ConversationView: View { | |
var messages: [Message] | |
var body: some View { | |
VStack(alignment: .leading, spacing: 4) { | |
ForEach(messages.indices) { i in | |
MessageView(message: self.messages[i]) | |
} | |
} // | |
.equalWidthHost() // <-- THIS IS THE NEW MODIFIER | |
} | |
} | |
let convo: [Message] = [ | |
.init(sender: "Peanutsmasher", body: "How do I get the size (width/height) of an UI element after its been rendered and pass it back to the parent for re-rendering?"), | |
.init(sender: "Rob", body: "First, it's worth understanding blah blah blah…"), | |
] | |
PlaygroundPage.current.setLiveView( | |
ConversationView(messages: convo) | |
.frame(width: 480) | |
.padding(12) | |
.border(Color.black) | |
.padding(12) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment