-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add better error handling, marquee text, and fied SSL networking
- Loading branch information
Showing
9 changed files
with
305 additions
and
67 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// | ||
// ErrorView.swift | ||
// ReveilleRides | ||
// | ||
// Created by Brandon Wees on 8/2/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct ErrorView: View { | ||
var text: String | ||
|
||
var body: some View { | ||
VStack { | ||
Image(systemName: "exclamationmark.triangle.fill") | ||
.foregroundStyle(.gray) | ||
Text(text) | ||
.multilineTextAlignment(.center) | ||
.foregroundStyle(.gray) | ||
} | ||
} | ||
} | ||
|
||
#Preview { | ||
ErrorView(text: "There was an error") | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict/> | ||
</plist> | ||
<dict> | ||
<key>NSAppTransportSecurity</key> | ||
<dict> | ||
<key>NSAllowsArbitraryLoads</key> | ||
<true/> | ||
</dict> | ||
</dict> | ||
</plist> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import SwiftUI | ||
import Combine | ||
import Foundation | ||
|
||
public struct MarqueeText : View { | ||
public var text: String | ||
public var font: UIFont | ||
public var leftFade: CGFloat | ||
public var rightFade: CGFloat | ||
public var startDelay: Double | ||
public var alignment: Alignment | ||
|
||
@State private var animate = false | ||
var isCompact = false | ||
|
||
public var body : some View { | ||
let stringWidth = text.widthOfString(usingFont: font) | ||
let stringHeight = text.heightOfString(usingFont: font) | ||
|
||
let animation = Animation | ||
.linear(duration: Double(stringWidth) / 30) | ||
.delay(startDelay) | ||
.repeatForever(autoreverses: false) | ||
|
||
let nullAnimation = Animation | ||
.linear(duration: 0) | ||
|
||
return ZStack { | ||
GeometryReader { geo in | ||
if stringWidth > geo.size.width { // don't use self.animate as conditional here | ||
Group { | ||
Text(self.text) | ||
.lineLimit(1) | ||
.font(.init(font)) | ||
.offset(x: self.animate ? -stringWidth - stringHeight * 2 : 0) | ||
.animation(self.animate ? animation : nullAnimation, value: self.animate) | ||
.onAppear { | ||
DispatchQueue.main.async { | ||
self.animate = geo.size.width < stringWidth | ||
} | ||
} | ||
.fixedSize(horizontal: true, vertical: false) | ||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) | ||
|
||
Text(self.text) | ||
.lineLimit(1) | ||
.font(.init(font)) | ||
.offset(x: self.animate ? 0 : stringWidth + stringHeight * 2) | ||
.animation(self.animate ? animation : nullAnimation, value: self.animate) | ||
.onAppear { | ||
DispatchQueue.main.async { | ||
self.animate = geo.size.width < stringWidth | ||
} | ||
} | ||
.fixedSize(horizontal: true, vertical: false) | ||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) | ||
} | ||
.onValueChanged(of: self.text, perform: {text in | ||
self.animate = geo.size.width < stringWidth | ||
}) | ||
|
||
.offset(x: leftFade) | ||
.mask( | ||
HStack(spacing:0) { | ||
Rectangle() | ||
.frame(width:2) | ||
.opacity(0) | ||
LinearGradient(gradient: Gradient(colors: [Color.black.opacity(0), Color.black]), startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/) | ||
.frame(width:leftFade) | ||
LinearGradient(gradient: Gradient(colors: [Color.black, Color.black]), startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/) | ||
LinearGradient(gradient: Gradient(colors: [Color.black, Color.black.opacity(0)]), startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/) | ||
.frame(width:rightFade) | ||
Rectangle() | ||
.frame(width:2) | ||
.opacity(0) | ||
}) | ||
.frame(width: geo.size.width + leftFade) | ||
.offset(x: leftFade * -1) | ||
} else { | ||
Text(self.text) | ||
.font(.init(font)) | ||
.onValueChanged(of: self.text, perform: {text in | ||
self.animate = geo.size.width < stringWidth | ||
}) | ||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: alignment) | ||
} | ||
} | ||
} | ||
.frame(height: stringHeight) | ||
.frame(maxWidth: isCompact ? stringWidth : nil) | ||
.onDisappear { | ||
self.animate = false | ||
} | ||
} | ||
|
||
public init(text: String, font: UIFont, leftFade: CGFloat, rightFade: CGFloat, startDelay: Double, alignment: Alignment? = nil) { | ||
self.text = text | ||
self.font = font | ||
self.leftFade = leftFade | ||
self.rightFade = rightFade | ||
self.startDelay = startDelay | ||
self.alignment = alignment != nil ? alignment! : .topLeading | ||
} | ||
} | ||
|
||
extension MarqueeText { | ||
public func makeCompact(_ compact: Bool = true) -> Self { | ||
var view = self | ||
view.isCompact = compact | ||
return view | ||
} | ||
} | ||
|
||
extension String { | ||
|
||
func widthOfString(usingFont font: UIFont) -> CGFloat { | ||
let fontAttributes = [NSAttributedString.Key.font: font] | ||
let size = self.size(withAttributes: fontAttributes) | ||
return size.width | ||
} | ||
|
||
func heightOfString(usingFont font: UIFont) -> CGFloat { | ||
let fontAttributes = [NSAttributedString.Key.font: font] | ||
let size = self.size(withAttributes: fontAttributes) | ||
return size.height | ||
} | ||
} | ||
|
||
extension View { | ||
/// A backwards compatible wrapper for iOS 14 `onChange` | ||
@ViewBuilder func onValueChanged<T: Equatable>(of value: T, perform onChange: @escaping (T) -> Void) -> some View { | ||
if #available(iOS 14.0, *) { | ||
self.onChange(of: value, perform: onChange) | ||
} else { | ||
self.onReceive(Just(value)) { (value) in | ||
onChange(value) | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.