Skip to content

Commit

Permalink
Feat SwiftUI Hint (#269)
Browse files Browse the repository at this point in the history
* Add Tooltip view

* Refactor Tooltip style

* Can adjust tooltip position

* Add tooltip playground for testing

* Refine layout tips logic

* Refine layout logic

* Update CharcoalTooltip.swift

* Refine tooltip spacing

* Add CharcoalIdentifiableOverlayView

* Use Actor to prevent Data Race

* Remove CharcoalIdentifiableOverlayView out

* Clean code

* Only update view when it is isPresenting

* Clean access control

* Add TooltipsView

* Fix tooltipY layout logic

* Use main actor and remove CharcoalContainerManagerKey

* Fix access control on CharcoalContainerManager

* Make viewID as @State

* Use EnviromentObject to create CharcoalContainerManager for each container

* Use ObservedObject on  CharcoalContainerManager

* Add use charcoal button as demo trigger

* Add arrow logic on tooltip

* Refine arrow logic

* Refine arrow layout logic

* Use StateObject to prevent unexpected reinit

* Refactor TooltipBubbleShape

* Fix edge layout logic

* Add comment

* Format code

* Use new approach to remove adaptiveMaxWidth

* Fix the tip bubble's position latency

* Add dismiss when interaction

* Reformat

* Add initial Snackbar

* Add thumbnail image

* Add support for thumbnailImage and action

* Clean code

* Reformat code

* Rename ActionContent

* Replace thumbnailImage type

* Add dismissOnTouchOutside control

* Add comment on CharcoalIdentifiableOverlayView

* Update CharcoalTooltip.swift

* Add SnackBar demo

* Replace thumbnail with charcoal logo

* Use @ViewBuilder

* Clean Code

* Made code more readable

* Update ToastsView.swift

* Add auto dismiss logic

* Fix dismiss comment

* Add Identifiable to CharcoalIdentifiableOverlayView

* Make all CharcoalPopupView identifiable

* Move all control logic into CharcoalPopupView

* Reformat

* Refine CharcoalOverlayContainerChild logic of updating view

* Rename to CharcoalOverlayUpdaterContainer

* Add CharcoalToast

* Refine toast control

* Refine screen edge of toast

* Refine comments

* Rename CharcoalPopupProtocol

* Refine isActuallyPresenting logic

* Clean animation

* Add animation configuration

* Add custom animation

* Add CharcoalToastProtocol

* Makes CharcoalSnackBar adapt CharcoalToastProtocol

* Remove time delay

* Refine SnackBar Animation logic

* Add CharcoalToastAnimationModifier

* Reformat code

* Update CharcoalPopupViewEdge of direction

* Refine demo

* Fix missing animation

* Rename charcoalAnimatedToast to charcoalAnimatableToast

* Rename CharcoalAnimatableToastProtocol

* Rename for clean

* Simplify protocols

* Add drag control

* Add Dismiss timer control logic

* Refine drag damping logic

* Add CharcoalToastDraggable

* Use CharcoalToastDraggableModifier on CharcoalSnackBar

* Format code

* Add init structure

* Add action button

* Replace placeholder with ja text

* Use CGPath union for iOS 16

* Update TooltipBubbleShape.swift

* Update CharcoalTooltip.swift

* Use GeometryReader on overlay

* Use new path draw logic

* Refine arrow width

* Refine arrow width

* Clean Code

* Refine preview

* Use timer instead of DispatchQueue

* Refine layout logic

* Refine arrow logic

* Refine layout logic

* Add missing charcoalOverlayContainer

* Add Balloon to examples

* Add default tutorials

* Remove overlay when disappear

* Refine charcoalOverlayContainer place

* Refine

* Update CharcoalSnackBar.swift

* Add Charcoal Hint

* Use surface 3

* Add isPresenting

* Add hints to sample

* Format code

* Init CharcoalTooltipView

* Init Bubble shape

* Refine tooltip preview

* Rename as Charcoal Bubble Shape

* Add Label to tooltip

* Update text frame when traitCollection did change

* Update CharcoalTooltipView.swift

* Add CharcoalTooltip

* Can debug show on method

* Can layout point

* Can redraw target point

* Update CharcoalTooltip.swift

* Refine tooltip display

* Share the logic

* Use interaction mode

* Use CharcoalOverlayContainerView

* Update CharcoalOverlay.swift

* Refactor to ChacoalOverlayManager

* Makes CharcoalIdentifiableOverlayView  Identifiable

* Refine layout logic

* Add display(view: CharcoalIdentifiableOverlayView)

* Add tooltip to uikit example

* Update Tooltips.swift

* Add CharcoalIdentifiableOverlayDelegate

* Reformat

* Update StringExtension.swift

* Add to UIKitSample

* Fix public requirements

* Reformat

* Use touch began to handle dismiss on touch

* Update CharcoalIdentifiableOverlayView.swift

* Add CharcoalToastView

* change cornerRadius

* Add CharcoalToast

* Move show logic out

* Reformat

* Refine dismiss method

* Update CharcoalToast.swift

* Add ActionContent and ActionComplete callback

* Update CharcoalToast.swift

* Update CharcoalBubbleShape_UIKit.swift

* Refine layout animation logic

* Refine animation

* Add dismiss

* Add example

* Reformat

* Add toasts example

* Add CharcoalSnackBarView

* Update project.pbxproj

* Clean code

* Refine layout logic

* Add CharcoalSnackBar

* Update CharcoalSnackBar.swift

* Update CharcoalSnackBar.swift

* Refine layout logic

* Refine toasts text

* Update CharcoalToastDraggableModifier.swift

* Add CharcoalRubberGesture

* use id to notify did dismiss

* Add rubber gesture

* Refactor

* Refactor

* Fix memory leak

* Format

* Fix memory leak

* Add example

* Refine snackbar

* Refine swift lint

* Refactor CharcoalToastView

* Refactor CharcoalSnackBarView

* Reformat

* Refine self logic

* Delete スクリーンショット 2024-02-21 17.28.10.png

* Remove duplicated balloons

* Delete スクリーンショット 2024-02-21 17.28.10.png

* Clean extensions

* Clean CharcoalRubberGesture

* Delete ConditionalViewModifier.swift

* Refactor Preview

* Fix h spacing

* Fix info16 icon

* Use CharcoalAction

* Reformat

* Omit trailing closure
  • Loading branch information
kevinneko authored Oct 15, 2024
1 parent 95af5cc commit 53e42d7
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public struct ContentView: View {
NavigationLink(destination: ToastsView().charcoalOverlayContainer()) {
Text("Toasts")
}
NavigationLink(destination: HintsView()) {
Text("Hints")
}
NavigationLink(destination: SpinnersView()) {
Text("Spinners")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Charcoal
import SwiftUI

public struct HintsView: View {
@State var isPresented = true

@State var isPresented2 = true

@State var isPresented3 = true

public var body: some View {
ScrollView {
VStack {
CharcoalHint(text: "ヒントテキストヒントテキスト", isPresenting: $isPresented, action: CharcoalAction(title: "Button") {
isPresented = false
})

CharcoalHint(text: "ヒントテキストヒントテキスト", isPresenting: $isPresented2)

CharcoalHint(text: "ヒントテキストヒントテキスト", maxWidth: .infinity, isPresenting: $isPresented3)
}
.padding()
}
.navigationBarTitle("Hints")
}
}

@available(iOS 17, *)
#Preview {
HintsView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ extension BalloonsViewController: UITableViewDelegate, UITableViewDataSource {
case .leading:
// use on: self.view to stick the balloon to the view
// so balloons will dismiss with the view
CharcoalBalloon.show(text: titleCase.text, anchorView: cell.leadingImageView, on: self.view)
CharcoalBalloon.show(text: titleCase.text, anchorView: cell.leadingImageView, on: view)
case .trailing:
CharcoalBalloon.show(text: titleCase.text, anchorView: cell.accessoryImageView, on: self.view)
CharcoalBalloon.show(text: titleCase.text, anchorView: cell.accessoryImageView, on: view)
case .bottom:
CharcoalBalloon.show(text: titleCase.text, anchorView: bottomInfoImage, on: self.view)
CharcoalBalloon.show(text: titleCase.text, anchorView: bottomInfoImage, on: view)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
public typealias ActionCallback = () -> Void

public struct CharcoalAction {
let title: String
let actionCallback: ActionCallback
public let title: String
public let actionCallback: ActionCallback

public init(title: String, actionCallback: @escaping ActionCallback) {
self.title = title
Expand Down
7 changes: 0 additions & 7 deletions Sources/CharcoalShared/Extensions/CGSize+Extension.swift

This file was deleted.

86 changes: 86 additions & 0 deletions Sources/CharcoalSwiftUI/Components/Hint/CharcoalHint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import SwiftUI

public struct CharcoalHint: View {
/// The text of the tooltip
let text: String

/// The text of the tooltip
let subtitle: String?

let icon: CharcoalAsset.Images = .info16

/// The corner radius of the tooltip
let cornerRadius: CGFloat = 8

let maxWidth: CGFloat?

/// A binding to whether the overlay is presented.
@Binding var isPresenting: Bool

let action: CharcoalAction?

@State var timer: Timer?

public init(
text: String,
subtitle: String? = nil,
maxWidth: CGFloat? = nil,
isPresenting: Binding<Bool>,
action: CharcoalAction? = nil
) {
self.text = text
self.subtitle = subtitle
self.maxWidth = maxWidth
_isPresenting = isPresenting
self.action = action
}

public var body: some View {
if isPresenting {
HStack(spacing: 4) {
Image(charocalIcon: icon)

VStack {
Text(text).charcoalTypography14Regular()
if let subtitle = subtitle {
Text(subtitle).charcoalTypography14Regular()
}
}

if let action = action {
Spacer()
Button(action: {
action.actionCallback()
}) {
Text(action.title)
}
.charcoalPrimaryButton(size: .small)
}
}
.frame(maxWidth: maxWidth)
.padding(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
.background(charcoalColor: .surface3)
.cornerRadius(cornerRadius, corners: .allCorners)
}
}
}

@available(iOS 17, *)
#Preview {
@Previewable @State var isPresenting = true
@Previewable @State var isPresenting2 = true
@Previewable @State var isPresenting3 = true

@Previewable @State var textOfLabel = "Hello"

VStack {
CharcoalHint(text: "ヒントテキストヒントテキスト", isPresenting: $isPresenting, action: CharcoalAction(title: "Button", actionCallback: {
isPresenting = false
}))

CharcoalHint(text: "ヒントテキストヒントテキスト", isPresenting: $isPresenting2)

CharcoalHint(text: "ヒントテキストヒントテキスト", maxWidth: .infinity, isPresenting: $isPresenting3)

}.padding()
}
5 changes: 2 additions & 3 deletions Sources/CharcoalSwiftUI/Extensions/Rect+Extension.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Foundation


extension CGRect {
// Calculate the intersection area of two rectangles
func intersectionArea(_ rect: CGRect) -> CGFloat {
let rect = self.intersection(rect)
let rect = intersection(rect)

return rect.width * rect.height
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class CharcoalAnchorTrackingView: UIView {
displayLink?.invalidate()
displayLink = nil
}

func tearDown() {
invalidate()
removeFromSuperview()
Expand Down
34 changes: 17 additions & 17 deletions Sources/CharcoalUIKit/Components/Balloon/CharcoalBalloon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ public class CharcoalBalloon {}

public extension CharcoalBalloon {
/**
Show a balloon anchored to a view.

- Parameters:
- text: The text to be displayed in the tooltip.
- anchorView: The view to which the tooltip will be anchored.
- interactionMode: The interaction mode of the tooltip. The default value is `.passThrough`.
- spacingToScreen: The spacing between the tooltip and the screen. The default value is `16`.
- gap: The spacing between the tooltip and the anchor view. The default value is `4`.
- on: The view on which the tooltip will be displayed. If not provided, the tooltip will be displayed on the window.
- Returns: The identifier of the tooltip.
# Example
```swift
CharcoalTooltip.show(text: "This is a tooltip", anchorView: someView)
```
*/
Show a balloon anchored to a view.

- Parameters:
- text: The text to be displayed in the tooltip.
- anchorView: The view to which the tooltip will be anchored.
- interactionMode: The interaction mode of the tooltip. The default value is `.passThrough`.
- spacingToScreen: The spacing between the tooltip and the screen. The default value is `16`.
- gap: The spacing between the tooltip and the anchor view. The default value is `4`.
- on: The view on which the tooltip will be displayed. If not provided, the tooltip will be displayed on the window.

- Returns: The identifier of the tooltip.

# Example
```swift
CharcoalTooltip.show(text: "This is a tooltip", anchorView: someView)
```
*/
@discardableResult
static func show(
text: String,
Expand Down

0 comments on commit 53e42d7

Please sign in to comment.