Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
117 changes: 0 additions & 117 deletions Sources/UIKitBackend/BaseWidget.swift

This file was deleted.

64 changes: 33 additions & 31 deletions Sources/UIKitBackend/UIKitBackend+Container.swift
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import SwiftCrossUI
import UIKit

final class ScrollWidget: WrapperWidget<UIScrollView> {
final class ScrollWidget: ContainerWidget {
private var scrollView = UIScrollView()
private var childWidthConstraint: NSLayoutConstraint?
private var childHeightConstraint: NSLayoutConstraint?

private let innerChild: BaseWidget
private lazy var contentLayoutGuideConstraints: [NSLayoutConstraint] = [
scrollView.contentLayoutGuide.leadingAnchor.constraint(equalTo: child.view.leadingAnchor),
scrollView.contentLayoutGuide.trailingAnchor.constraint(equalTo: child.view.trailingAnchor),
scrollView.contentLayoutGuide.topAnchor.constraint(equalTo: child.view.topAnchor),
scrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: child.view.bottomAnchor),
]

init(child innerChild: BaseWidget) {
self.innerChild = innerChild
super.init(child: UIScrollView())

child.addSubview(innerChild)
override func loadView() {
view = scrollView
scrollView.translatesAutoresizingMaskIntoConstraints = false
}

NSLayoutConstraint.activate([
innerChild.topAnchor.constraint(equalTo: child.contentLayoutGuide.topAnchor),
innerChild.bottomAnchor.constraint(equalTo: child.contentLayoutGuide.bottomAnchor),
innerChild.leftAnchor.constraint(equalTo: child.contentLayoutGuide.leftAnchor),
innerChild.rightAnchor.constraint(equalTo: child.contentLayoutGuide.rightAnchor),
])
override func viewWillLayoutSubviews() {
NSLayoutConstraint.activate(contentLayoutGuideConstraints)
super.viewWillLayoutSubviews()
}

func setScrollBars(
Expand All @@ -29,8 +31,8 @@ final class ScrollWidget: WrapperWidget<UIScrollView> {
case (true, true):
childHeightConstraint!.isActive = false
case (false, nil):
childHeightConstraint = innerChild.heightAnchor.constraint(
equalTo: child.heightAnchor)
childHeightConstraint = child.view.heightAnchor.constraint(
equalTo: scrollView.heightAnchor)
fallthrough
case (false, false):
childHeightConstraint!.isActive = true
Expand All @@ -42,68 +44,68 @@ final class ScrollWidget: WrapperWidget<UIScrollView> {
case (true, true):
childWidthConstraint!.isActive = false
case (false, nil):
childWidthConstraint = innerChild.widthAnchor.constraint(equalTo: child.widthAnchor)
childWidthConstraint = child.view.widthAnchor.constraint(
equalTo: scrollView.widthAnchor)
fallthrough
case (false, false):
childWidthConstraint!.isActive = true
default:
break
}

child.showsVerticalScrollIndicator = hasVerticalScrollBar
child.showsHorizontalScrollIndicator = hasHorizontalScrollBar
scrollView.showsVerticalScrollIndicator = hasVerticalScrollBar
scrollView.showsHorizontalScrollIndicator = hasHorizontalScrollBar
}
}

extension UIKitBackend {
public func createContainer() -> Widget {
BaseWidget()
BaseViewWidget()
}

public func removeAllChildren(of container: Widget) {
container.subviews.forEach { $0.removeFromSuperview() }
container.childWidgets.forEach { $0.removeFromParentWidget() }
}

public func addChild(_ child: Widget, to container: Widget) {
container.addSubview(child)
container.add(childWidget: child)
}

public func setPosition(
ofChildAt index: Int,
in container: Widget,
to position: SIMD2<Int>
) {
guard index < container.subviews.count else {
guard index < container.childWidgets.count else {
assertionFailure("Attempting to set position of nonexistent subview")
return
}

let child = container.subviews[index] as! BaseWidget
let child = container.childWidgets[index]
child.x = position.x
child.y = position.y
}

public func removeChild(_ child: Widget, from container: Widget) {
assert(child.isDescendant(of: container))
child.removeFromSuperview()
assert(child.view.isDescendant(of: container.view))
child.removeFromParentWidget()
}

public func createColorableRectangle() -> Widget {
BaseWidget()
BaseViewWidget()
}

public func setColor(ofColorableRectangle widget: Widget, to color: Color) {
widget.backgroundColor = color.uiColor
widget.view.backgroundColor = color.uiColor
}

public func setCornerRadius(of widget: Widget, to radius: Int) {
widget.layer.cornerRadius = CGFloat(radius)
widget.layer.masksToBounds = true
widget.setNeedsLayout()
widget.view.layer.cornerRadius = CGFloat(radius)
widget.view.layer.masksToBounds = true
}

public func naturalSize(of widget: Widget) -> SIMD2<Int> {
let size = widget.intrinsicContentSize
let size = widget.view.intrinsicContentSize
return SIMD2(
Int(size.width.rounded(.awayFromZero)),
Int(size.height.rounded(.awayFromZero))
Expand Down
6 changes: 3 additions & 3 deletions Sources/UIKitBackend/UIKitBackend+Control.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ final class TextFieldWidget: WrapperWidget<UITextField>, UITextFieldDelegate {
}
#endif

final class ClickableWidget: WrapperWidget<BaseWidget> {
final class ClickableWidget: ContainerWidget {
private var gestureRecognizer: UITapGestureRecognizer!
var onClick: (() -> Void)?

override init(child: BaseWidget) {
override init(child: some WidgetProtocol) {
super.init(child: child)

gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTouched))
gestureRecognizer.cancelsTouchesInView = true
child.addGestureRecognizer(gestureRecognizer)
child.view.addGestureRecognizer(gestureRecognizer)
}

@objc
Expand Down
2 changes: 1 addition & 1 deletion Sources/UIKitBackend/UIKitBackend+Picker.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SwiftCrossUI
import UIKit

protocol Picker: BaseWidget {
protocol Picker: WidgetProtocol {
func setOptions(to options: [String])
func setChangeHandler(to onChange: @escaping (Int?) -> Void)
func setSelectedOption(to index: Int?)
Expand Down
90 changes: 90 additions & 0 deletions Sources/UIKitBackend/UIKitBackend+SplitView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import UIKit

#if os(iOS)
final class SplitWidget: WrapperControllerWidget<UISplitViewController>,
UISplitViewControllerDelegate
{
var resizeHandler: (() -> Void)?
private let sidebarContainer: ContainerWidget
private let mainContainer: ContainerWidget

init(sidebarWidget: some WidgetProtocol, mainWidget: some WidgetProtocol) {
// UISplitViewController requires its children to be controllers, not views
sidebarContainer = ContainerWidget(child: sidebarWidget)
mainContainer = ContainerWidget(child: mainWidget)

super.init(child: UISplitViewController())

child.delegate = self

child.preferredDisplayMode = .oneBesideSecondary
child.preferredPrimaryColumnWidthFraction = 0.3

child.viewControllers = [sidebarContainer, mainContainer]
}

override func viewDidLoad() {
NSLayoutConstraint.activate([
sidebarContainer.view.leadingAnchor.constraint(
equalTo: sidebarContainer.child.view.leadingAnchor),
sidebarContainer.view.trailingAnchor.constraint(
equalTo: sidebarContainer.child.view.trailingAnchor),
sidebarContainer.view.topAnchor.constraint(
equalTo: sidebarContainer.child.view.topAnchor),
sidebarContainer.view.bottomAnchor.constraint(
equalTo: sidebarContainer.child.view.bottomAnchor),
mainContainer.view.leadingAnchor.constraint(
equalTo: mainContainer.child.view.leadingAnchor),
mainContainer.view.trailingAnchor.constraint(
equalTo: mainContainer.child.view.trailingAnchor),
mainContainer.view.topAnchor.constraint(
equalTo: mainContainer.child.view.topAnchor),
mainContainer.view.bottomAnchor.constraint(
equalTo: mainContainer.child.view.bottomAnchor),
])

super.viewDidLoad()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
resizeHandler?()
}
}

extension UIKitBackend {
public func createSplitView(
leadingChild: any WidgetProtocol,
trailingChild: any WidgetProtocol
) -> any WidgetProtocol {
precondition(
UIDevice.current.userInterfaceIdiom != .phone,
"NavigationSplitView is currently unsupported on iPhone and iPod touch.")

return SplitWidget(sidebarWidget: leadingChild, mainWidget: trailingChild)
}

public func setResizeHandler(
ofSplitView splitView: Widget,
to action: @escaping () -> Void
) {
let splitWidget = splitView as! SplitWidget
splitWidget.resizeHandler = action
}

public func sidebarWidth(ofSplitView splitView: Widget) -> Int {
let splitWidget = splitView as! SplitWidget
return Int(splitWidget.child.primaryColumnWidth.rounded(.toNearestOrEven))
}

public func setSidebarWidthBounds(
ofSplitView splitView: Widget,
minimum minimumWidth: Int,
maximum maximumWidth: Int
) {
let splitWidget = splitView as! SplitWidget
splitWidget.child.minimumPrimaryColumnWidth = CGFloat(minimumWidth)
splitWidget.child.maximumPrimaryColumnWidth = CGFloat(maximumWidth)
}
}
#endif
Loading
Loading