Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version: 1.1.0 #2

Merged
merged 5 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Add a dependency in your `Package.swift`

``` swift
dependencies: [
.package(url: "/~https://github.com/BaherTamer/SwiftSafeUI.git", from: "1.0.0")
.package(url: "/~https://github.com/BaherTamer/SwiftSafeUI.git", from: "1.1.0")
]
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// SafeAutocapitalization.swift
//
// GitHub Repo and Documentation: /~https://github.com/BaherTamer/SwiftSafeUI
//
// Copyright © 2024 Baher Tamer. All rights reserved.
//

import SwiftUI

extension View {
///
/// Config navigation bar visibility based on the provided `Bool` value. Handles deprecation logic for different iOS versions.
///
/// If the device is running iOS 18.0 or later,
/// it uses the new [``toolbarVisibility(_:for:)``](https://developer.apple.com/documentation/swiftui/view/toolbarvisibility(_:for:)) modifier.
/// If the device is running iOS 16.0 or later,
/// it uses the new [``toolbar(_:for:)``](https://developer.apple.com/documentation/swiftui/view/toolbar(_:for:)) modifier.
/// For earlier versions, it falls back to the old [``navigationBarHidden(_:)``](https://developer.apple.com/documentation/swiftui/view/navigationbarhidden(_:)) modifier.
///
/// - Parameters:
/// - isHidden: A `Bool` value that determines whether the navigation bar should be hidden or not.
///
/// - Returns: A `View` that reflects the visibility state of the navigation bar.
///
public func safeNavBarHidden(_ isHidden: Bool) -> some View {
modifier(
SafeNavBarHidden(isHidden: isHidden)
)
}
}

fileprivate struct SafeNavBarHidden: ViewModifier {
// MARK: - Inputs
let isHidden: Bool

// MARK: - Body
func body(content: Content) -> some View {
if #available(iOS 18.0, *) {
content
.toolbarVisibility(
isHidden ? .hidden : .automatic,
for: .navigationBar
)
} else if #available(iOS 16.0, *) {
content
.toolbar(
isHidden ? .hidden : .automatic,
for: .navigationBar
)
} else {
content
.navigationBarHidden(isHidden)
}
}
}
60 changes: 60 additions & 0 deletions Sources/SwiftSafeUI/Modifiers/Input & Event/SafeOnChange.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// SafeAutocapitalization.swift
//
// GitHub Repo and Documentation: /~https://github.com/BaherTamer/SwiftSafeUI
//
// Copyright © 2024 Baher Tamer. All rights reserved.
//

import SwiftUI

@available(iOS 14.0, *)
extension View {
///
/// Adds an action to perform when the given value changes. Handles deprecation logic for different iOS versions.
///
/// This modifier triggers an action when the specified value changes.
/// It ensures compatibility with different iOS versions by using the appropriate `onChange` modifier based on the iOS version.
///
/// If the device is running iOS 17.0 or later,
/// it uses the new [``onChange(of:initial:_:)``](https://developer.apple.com/documentation/swiftui/view/onchange(of:initial:_:)-4psgg) modifier.
/// For earlier versions, it falls back to the old [``onChange(of:perform:)``](https://developer.apple.com/documentation/swiftui/view/onchange(of:perform:)) modifier.
///
/// > Note: This modifier is available from iOS 14.0 or later.
///
/// - Parameters:
/// - value: The value to monitor for changes.
/// - action: A closure that is called with the old and new values when a change is detected.
///
/// - Returns: A view that applies the specified action when the value changes.
///
public func safeOnChange<Value: Equatable>(
_ value: Value,
perform action: @escaping (Value, Value) -> Void
) -> some View {
modifier(
SafeOnChange(
value: value,
action: action
)
)
}
}

@available(iOS 14.0, *)
fileprivate struct SafeOnChange<Value: Equatable>: ViewModifier {
let value: Value
var action: (Value, Value) -> Void

func body(content: Content) -> some View {
if #available(iOS 17.0, *) {
content
.onChange(of: value, action)
} else {
content
.onChange(of: value) { [value] newValue in
action(value, newValue)
}
}
}
}
83 changes: 83 additions & 0 deletions Sources/SwiftSafeUI/Modifiers/Text & Symbol/SafeLineLimit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// SafeAutocapitalization.swift
//
// GitHub Repo and Documentation: /~https://github.com/BaherTamer/SwiftSafeUI
//
// Copyright © 2024 Baher Tamer. All rights reserved.
//

import SwiftUI

extension View {
///
/// Applies a line limit to the current view, handling the logic for reserving space for different iOS versions.
///
/// This modifier applies the line limit and reserves space using the appropriate method based on the iOS version.
///
/// If the device is running iOS 16.0 or later,
/// it uses the [``lineLimit(_:reservesSpace:)``](https://developer.apple.com/documentation/swiftui/view/linelimit(_:reservesspace:)) modifier.
/// For earlier versions, it sets a fixed frame height to reserve space for the specified number of lines.
///
/// - Parameters:
/// - limit: The maximum number of lines for the text view.
/// - reservesSpace: A Boolean value indicating whether the view reserves space for the specified number of lines, even if fewer lines are displayed.
///
public func safeLineLimit(
_ limit: Int,
reservesSpace: Bool
) -> some View {
modifier(
SafeLineLimit(
limit: limit,
reservesSpace: reservesSpace
)
)
}
}

fileprivate struct SafeLineLimit: ViewModifier {
// MARK: - Inputs
let limit: Int
let reservesSpace: Bool

// MARK: - Variables
@State private var height: CGFloat = TextHeightPreferenceKey.defaultValue

// MARK: - Body
func body(content: Content) -> some View {
if #available(iOS 16.0, *) {
content
.lineLimit(limit, reservesSpace: reservesSpace)
} else {
content
.lineLimit(limit)
.background(geometryReader)
.frame(height: reservesSpace ? height : nil, alignment: .top)
.onPreferenceChange(TextHeightPreferenceKey.self) { height in
self.height = height * CGFloat(limit)
}
}
}

// MARK: - Private Helpers
private var geometryReader: some View {
GeometryReader { geometry in
Color.clear
.preference(
key: TextHeightPreferenceKey.self,
value: geometry.size.height
)
}
}
}

private struct TextHeightPreferenceKey: PreferenceKey {
// MARK: - Variables
static var defaultValue: CGFloat = 20

// MARK: - Config Functions
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}