Skip to content

Instantly share code, notes, and snippets.

@olgusirman
Created July 25, 2020 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olgusirman/9db9acbdf2682f0656f64a0a1dd78e34 to your computer and use it in GitHub Desktop.
Save olgusirman/9db9acbdf2682f0656f64a0a1dd78e34 to your computer and use it in GitHub Desktop.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
EventHeader()
ImagePlaceholder().layoutPriority(-1) // added
.frame( minHeight: 100)
Text(makeDescription())
//Text(makeDescription()).layoutPriority(1) // removed
Spacer()
//EventInfoList()
// EventInfoList().layoutPriority(1) // added
EventInfoList().fixedSize(horizontal: false, vertical: true)
}.padding()
}
}
extension ContentView {
func makeDescription() -> String {
String(repeating: "This is a description ", count: 50)
}
}
struct EventHeader: View {
var body: some View {
HStack(spacing: 15) {
CalendarView()
VStack(alignment: .leading) {
Text("Event title").font(.title)
Text("Location")
}
Spacer()
}
}
}
struct ImagePlaceholder: View {
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 10).stroke()
Text("Image placeholder")
}
}
}
struct EventInfoList: View {
var body: some View {
HeightSyncedRow(background: Color.secondary.cornerRadius(10)) {
EventInfoBadge(
iconName: "video.circle.fill",
text: "Video call available"
)
EventInfoBadge(
iconName: "doc.text.fill",
text: "Files are attached"
)
EventInfoBadge(
iconName: "person.crop.circle.badge.plus",
text: "Invites enabled, 5 people maximum"
)
}
}
}
struct EventInfoBadge: View {
var iconName: String
var text: String
var body: some View {
VStack {
Image(systemName: iconName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
Text(text)
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
}
.padding(.vertical, 10)
.padding(.horizontal, 5)
}
}
struct CalendarView: View {
var eventIsVerified = true
var body: some View {
Image(systemName: "calendar")
.resizable()
.frame(width: 50, height: 50)
.padding()
.background(Color.red)
.cornerRadius(10)
.foregroundColor(.white)
.addVerifiedBadge(eventIsVerified)
}
}
private struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = nextValue()
}
}
extension View {
func syncingHeightIfLarger(than height: Binding<CGFloat?>) -> some View {
background(GeometryReader { proxy in
// We have to attach our preference assignment to
// some form of view, so we just use a clear color
// here to make that view completely transparent:
Color.clear.preference(
key: HeightPreferenceKey.self,
value: proxy.size.height
)
})
.onPreferenceChange(HeightPreferenceKey.self) {
height.wrappedValue = max(height.wrappedValue ?? 0, $0)
}
}
}
struct HeightSyncedRow<Background: View, Content: View>: View {
private let background: Background
private let content: Content
@State private var childHeight: CGFloat?
init(background: Background,
@ViewBuilder content: () -> Content) {
self.background = background
self.content = content()
}
var body: some View {
HStack {
content.syncingHeightIfLarger(than: $childHeight)
.frame(height: childHeight)
.background(background)
}
}
}
extension View {
func addVerifiedBadge(_ isVerified: Bool) -> some View {
ZStack(alignment: .topTrailing) {
self
if isVerified {
Image(systemName: "checkmark.circle.fill")
.alignAsBadge()
}
}
}
func alignAsBadge(withRatio ratio: CGFloat = 0.8,
alignment: Alignment = .topTrailing) -> some View {
alignmentGuide(alignment.horizontal) {
$0.width * ratio
}
.alignmentGuide(alignment.vertical) {
// Here we first align our view's bottom edge
// according to its host view's top edge,
// and we then subtract 80% of its height.
$0[.bottom] - $0.height * ratio
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment