Skip to content

Commit

Permalink
Version: 1.1.0 (#2)
Browse files Browse the repository at this point in the history
* chore: add safe line limit modifier

* chore: add safe on change modifier

* chore: add safe nav bar hidden modifier

* chore: update package version in readme file

* fix: on change ios version issue
  • Loading branch information
BaherTamer authored Sep 21, 2024
1 parent 98d4881 commit 6baf8f6
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 1 deletion.
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())
}
}

0 comments on commit 6baf8f6

Please sign in to comment.