Skip to content

Commit

Permalink
Fixes a bug where inspect couldn't find some scrollviews
Browse files Browse the repository at this point in the history
Fixes a bug where parentController could result in an infinite loop
Disabling scrolling now works as expected
Introduces preliminary ShareLink API + ActivityView API for showing the sheet
Label now correctly drops the text in navigation bar contexts
Various demo updates
  • Loading branch information
shaps80 committed Sep 29, 2022
1 parent edd8426 commit c036065
Show file tree
Hide file tree
Showing 17 changed files with 655 additions and 66 deletions.
3 changes: 0 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ let package = Package(
targets: ["SwiftUIBackports"]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
],
targets: [
.target(name: "SwiftUIBackports")
],
Expand Down
1 change: 0 additions & 1 deletion Sources/SwiftUIBackports/Extras/FittingScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import SwiftUI

/// A scrollview that behaves more similarly to a `VStack` when its content size is small enough.
public struct FittingScrollView<Content: View>: View {

private let content: Content
private let showsIndicators: Bool

Expand Down
8 changes: 7 additions & 1 deletion Sources/SwiftUIBackports/Internal/Inspect+UIKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ internal extension PlatformView {
views.remove(at: index)

for subview in views.reversed() {
if let typed = subview.child(ofType: type) {
if let typed = subview as? ViewType {
return typed
} else if let typed = subview.child(ofType: type) {
return typed
}
}
Expand Down Expand Up @@ -76,6 +78,10 @@ internal struct Inspector {
func sibling<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
hostView.sibling(ofType: ViewType.self)
}

func child<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
hostView.child(ofType: ViewType.self)
}
}

extension View {
Expand Down
28 changes: 12 additions & 16 deletions Sources/SwiftUIBackports/Internal/OwningController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import UIKit
public extension UIView {

var parentController: UIViewController? {
var responder: UIResponder? = self

while !(responder is UIViewController) && superview != nil {
if let next = responder?.next {
responder = next
}
if let responder = self.next as? UIViewController {
return responder
} else if let responder = self.next as? UIView {
return responder.parentController
} else {
return nil
}

return responder as? UIViewController
}

}
Expand All @@ -24,15 +22,13 @@ import AppKit
public extension NSView {

var parentController: NSViewController? {
var responder: NSResponder? = self

while !(responder is NSViewController) && superview != nil {
if let next = responder?.nextResponder {
responder = next
}
if let responder = self.next as? NSViewController {
return responder
} else if let responder = self.next as? NSView {
return responder.parentController
} else {
return nil
}

return responder as? NSViewController
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import SwiftUI

public struct ProposedViewSize: Equatable, Sendable {
public var width: CGFloat?
public var height: CGFloat?

public static let zero = Self(width: 0, height: 0)
public static let infinity = Self(width: .infinity, height: .infinity)
public static let unspecified = Self(width: nil, height: nil)

public init(_ size: CGSize) {
self.width = size.width
self.height = size.height
}

public init(width: CGFloat?, height: CGFloat?) {
self.width = width
self.height = height
}

public func replacingUnspecifiedDimensions(by size: CGSize) -> CGSize {
.init(
width: width ?? size.width,
height: height ?? size.height
)
}
}
76 changes: 76 additions & 0 deletions Sources/SwiftUIBackports/Shared/ImageRenderer/Renderer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import SwiftUI

public final class ImageRenderer<Content>: ObservableObject where Content: View {
public var content: Content
public var label: String?
public var proposedSize: ProposedViewSize = .unspecified
public var scale: CGFloat = UIScreen.main.scale
public var isOpaque: Bool = false
public var colorMode: ColorRenderingMode = .nonLinear

public init(content: Content) {
self.content = content
}
}

//public extension ImageRenderer {
// func render(rasterizationScale: CGFloat = 1, renderer: (CGSize, (CGContext) -> Void) -> Void) {
//
// }
//}

public extension ImageRenderer {
var cgImage: CGImage? {
#if os(macOS)
nsImage?.cgImage
#else
uiImage?.cgImage
#endif
}

#if os(macOS)

var nsImage: NSImage? {
nil
}

#else

var uiImage: UIImage? {
let controller = UIHostingController(rootView: content)
let size = controller.view.intrinsicContentSize
controller.view.bounds = CGRect(origin: .zero, size: size)
controller.view.backgroundColor = .clear

let format = UIGraphicsImageRendererFormat(for: controller.traitCollection)
format.opaque = isOpaque
format.preferredRange = colorMode.range
format.scale = scale

let renderer = UIGraphicsImageRenderer(size: size, format: format)

let image = renderer.image { context in
controller.view.drawHierarchy(in: context.format.bounds, afterScreenUpdates: true)
}

image.accessibilityLabel = label
objectWillChange.send()

return image
}

#endif
}

#if os(macOS)
#else
extension ColorRenderingMode {
var range: UIGraphicsImageRendererFormat.Range {
switch self {
case .extendedLinear: return .extended
case .linear: return .standard
default: return .automatic
}
}
}
#endif
15 changes: 9 additions & 6 deletions Sources/SwiftUIBackports/Shared/Label/Label.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,18 @@ extension Backport where Wrapped == Any {

/// Creates a label with a custom title and icon.
public init(@ViewBuilder title: () -> Title, @ViewBuilder icon: () -> Icon) {
config = .init(title: .init(content: title()), icon: .init(content: icon()))
config = .init(title: .init(title()), icon: .init(icon()))
}

@MainActor public var body: some View {
if let style = style {
style.makeBody(configuration: config.environment(environment))
if #available(iOS 14, *) {
SwiftUI.Label {
config.title
} icon: {
config.icon
}
} else {
DefaultLabelStyle()
.makeBody(configuration: config.environment(environment))
style.makeBody(configuration: config.environment(environment))
}
}
}
Expand Down Expand Up @@ -156,7 +159,7 @@ extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image {

}

extension Backport.Label where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon {
extension Backport.Label where Wrapped == Any {

/// Creates a label representing the configuration of a style.
///
Expand Down
22 changes: 2 additions & 20 deletions Sources/SwiftUIBackports/Shared/Label/LabelConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,11 @@ extension Backport where Wrapped == Any {
/// The properties of a label.
public struct LabelStyleConfiguration {

/// A type-erased title view of a label.
public struct Title: View {
let content: AnyView
public var body: some View { content }
init<Content: View>(content: Content) {
self.content = .init(content)
}
}

/// A type-erased icon view of a label.
public struct Icon: View {
let content: AnyView
public var body: some View { content }
init<Content: View>(content: Content) {
self.content = .init(content)
}
}

/// A description of the labeled item.
public internal(set) var title: LabelStyleConfiguration.Title
public internal(set) var title: AnyView

/// A symbolic representation of the labeled item.
public internal(set) var icon: LabelStyleConfiguration.Icon
public internal(set) var icon: AnyView

internal var environment: EnvironmentValues = .init()

Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIBackports/Shared/Label/LabelStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ internal struct AnyLabelStyle: BackportLabelStyle {
}

private struct BackportLabelStyleEnvironmentKey: EnvironmentKey {
static var defaultValue: AnyLabelStyle? = nil
static var defaultValue: AnyLabelStyle = .init(.automatic)
}

internal extension EnvironmentValues {
var backportLabelStyle: AnyLabelStyle? {
var backportLabelStyle: AnyLabelStyle {
get { self[BackportLabelStyleEnvironmentKey.self] }
set { self[BackportLabelStyleEnvironmentKey.self] = newValue }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ extension Backport where Wrapped == Any {
///
/// You can also use ``LabelStyle/automatic`` to construct this style.
public struct DefaultLabelStyle: BackportLabelStyle {
private struct Label: View {
let configuration: Configuration
@State private var isToolbarElement: Bool = false

var body: some View {
if isToolbarElement {
IconOnlyLabelStyle().makeBody(configuration: configuration)
} else {
TitleAndIconLabelStyle().makeBody(configuration: configuration)
.inspect { inspector in
inspector.ancestor(ofType: UINavigationBar.self)
} customize: { _ in
isToolbarElement = true
}
}
}
}

public init() { }

Expand All @@ -19,11 +36,8 @@ extension Backport where Wrapped == Any {
/// hierarchy where this style is the current label style.
///
/// - Parameter configuration: The properties of the label.
public func makeBody(configuration: DefaultLabelStyle.Configuration) -> some View {
HStack {
configuration.icon
configuration.title
}
public func makeBody(configuration: Configuration) -> some View {
Label(configuration: configuration)
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension Backport where Wrapped: View {
content
.environment(\.backportRefresh, Backport<Any>.RefreshAction(action))
.inspect { inspector in
inspector.sibling(ofType: UITableView.self)
inspector.sibling(ofType: UIScrollView.self)
} customize: { scrollView in
guard scrollView.refreshControl == nil else { return }
scrollView.refreshControl = RefreshControl {
Expand Down
Loading

0 comments on commit c036065

Please sign in to comment.