Skip to content

Commit 82feba9

Browse files
committed
fix: handle weird unresponsiveness on FavoriteView
1 parent e3c00a9 commit 82feba9

File tree

8 files changed

+114
-81
lines changed

8 files changed

+114
-81
lines changed

Giffy/App/Router/Routing.swift

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,67 +19,69 @@ public final class Routing<T: RouterIdentifiable> {
1919
public var onPresent: ((T, Bool) -> Void)?
2020
public var onPopLast: ((Int, Bool) -> Void)?
2121
public var onPopToRoot: ((Int?, Bool) -> Void)?
22-
22+
2323
public var currentRoutes: [T] {
2424
return routes
2525
}
26-
26+
2727
public init(initial: T? = nil) {
2828
if let initial = initial { push(initial) }
2929
}
30-
30+
3131
public func makeRoot(_ route: T, animated: Bool = true, needValidate: Bool = false) {
3232
guard !(needValidate && routes.last?.key == route.key) else { return }
3333
routes = [route]
3434
onMakeRoot?(route, animated)
3535
}
36-
36+
3737
public func push(_ route: T, animated: Bool = true, needValidate: Bool = false) {
3838
guard !(needValidate && routes.last?.key == route.key) else { return }
3939
routes.append(route)
40+
print("Routing pushed: \(route)")
4041
onPush?(route, animated)
4142
}
42-
43+
4344
public func present(_ route: T, animated: Bool = true, needValidate: Bool = false) {
4445
guard !(needValidate && routes.last?.key == route.key) else { return }
4546
routes.append(route)
47+
print("Routing presented: \(route)")
4648
onPresent?(route, animated)
4749
}
48-
50+
4951
public func pop(animated: Bool = true) {
5052
guard !routes.isEmpty else { return }
5153
let popped = routes.removeLast()
5254
print("Routing popped: \(popped)")
5355
onPopLast?(1, animated)
5456
}
55-
57+
5658
public func popTo(last index: Int, animated: Bool = true) {
5759
guard !routes.isEmpty else { return }
5860
let elementsToRemove = min(index - 1, routes.count - 1)
5961
routes.removeLast(elementsToRemove)
6062
onPopLast?(index, animated)
6163
}
62-
64+
6365
public func popTo(_ route: T, inclusive: Bool = false, animated: Bool = true) {
6466
guard let foundIndex = routes.lastIndex(where: { $0 == route }) else { return }
6567
let indexToPopTo = inclusive ? foundIndex : foundIndex + 1
6668
guard indexToPopTo != 0 else {
6769
popToRoot(index: nil, animated: animated)
6870
return
6971
}
70-
72+
7173
let numToPop = routes.count - indexToPopTo
7274
routes.removeLast(numToPop)
7375
onPopLast?(numToPop, animated)
7476
}
75-
77+
7678
public func popToRoot(index: Int? = nil, animated: Bool = true) {
7779
onPopToRoot?(index, animated)
7880
if routes.count > 1 {
7981
routes.removeSubrange(1 ..< routes.count)
8082
}
8183
}
82-
84+
8385
public func onSystemPop() {
8486
guard !routes.isEmpty else { return }
8587
let popped = routes.removeLast()
@@ -90,12 +92,12 @@ public final class Routing<T: RouterIdentifiable> {
9092
public struct RouteProvider<T: RouterIdentifiable, Screen: View>: View {
9193
private let router: Routing<T>
9294
@ViewBuilder private let routeMap: (T) -> Screen
93-
95+
9496
public init(_ router: Routing<T>, @ViewBuilder _ routeMap: @escaping (T) -> Screen) {
9597
self.router = router
9698
self.routeMap = routeMap
9799
}
98-
100+
99101
public var body: some View {
100102
NavigationControllerHost(router: router, routeMap: routeMap)
101103
.edgesIgnoringSafeArea(.all)
@@ -105,35 +107,35 @@ public struct RouteProvider<T: RouterIdentifiable, Screen: View>: View {
105107
struct NavigationControllerHost<T: RouterIdentifiable, Screen: View>: UIViewControllerRepresentable {
106108
let router: Routing<T>
107109
@ViewBuilder var routeMap: (T) -> Screen
108-
110+
109111
func makeUIViewController(context: Context) -> PopAwareUINavigationController {
110112
let navigation = PopAwareUINavigationController()
111113
navigation.navigationBar.isHidden = true
112114
setupRouterCallbacks(in: navigation)
113115
setupInitialRoutes(in: navigation)
114116
return navigation
115117
}
116-
118+
117119
private func setupRouterCallbacks(in navigation: PopAwareUINavigationController) {
118120
navigation.popHandler = { router.onSystemPop() }
119121
navigation.stackSizeProvider = { router.currentRoutes.count }
120-
122+
121123
router.onMakeRoot = { route, animated in
122124
let viewController = UIHostingController(rootView: routeMap(route))
123125
navigation.setViewControllers([viewController], animated: animated)
124126
}
125-
127+
126128
router.onPush = { route, animated in
127129
let viewController = UIHostingController(rootView: routeMap(route))
128130
navigation.pushViewController(viewController, animated: animated)
129131
}
130-
132+
131133
router.onPresent = { route, animated in
132134
let viewController = UIHostingController(rootView: routeMap(route))
133135
viewController.modalPresentationStyle = .overFullScreen
134-
navigation.present(viewController, animated: animated)
136+
rootViewController?.present(viewController, animated: animated)
135137
}
136-
138+
137139
router.onPopLast = { numToPop, animated in
138140
let popTo = navigation.viewControllers.count - numToPop - 1
139141
if numToPop == navigation.viewControllers.count {
@@ -142,7 +144,7 @@ struct NavigationControllerHost<T: RouterIdentifiable, Screen: View>: UIViewCont
142144
navigation.popToViewController(navigation.viewControllers[popTo], animated: animated)
143145
}
144146
}
145-
147+
146148
router.onPopToRoot = { tabIndex, animated in
147149
navigation.popToRootViewController(animated: animated)
148150
if let tabIndex = tabIndex {
@@ -154,34 +156,53 @@ struct NavigationControllerHost<T: RouterIdentifiable, Screen: View>: UIViewCont
154156
}
155157
}
156158
}
157-
159+
160+
private var rootViewController: UIViewController? {
161+
UIApplication.shared.connectedScenes
162+
.compactMap { $0 as? UIWindowScene }
163+
.compactMap { $0.windows.first { $0.isKeyWindow } }
164+
.first?.rootViewController.flatMap(getTopViewController)
165+
}
166+
167+
private func getTopViewController(from viewController: UIViewController) -> UIViewController {
168+
if let presented = viewController.presentedViewController {
169+
return getTopViewController(from: presented)
170+
} else if let navigation = viewController as? UINavigationController {
171+
return getTopViewController(from: navigation.visibleViewController ?? viewController)
172+
} else if let tabBar = viewController as? UITabBarController {
173+
return getTopViewController(from: tabBar.selectedViewController ?? viewController)
174+
} else {
175+
return viewController
176+
}
177+
}
178+
158179
private func setupInitialRoutes(in navigation: PopAwareUINavigationController) {
159180
for path in router.currentRoutes {
160181
navigation.pushViewController(UIHostingController(rootView: routeMap(path)), animated: true)
161182
}
162183
}
163-
184+
164185
func updateUIViewController(_ navigation: PopAwareUINavigationController, context: Context) {
165186
navigation.navigationBar.isHidden = true
166187
}
167-
188+
168189
static func dismantleUIViewController(_ navigation: PopAwareUINavigationController, coordinator: ()) {
169190
navigation.viewControllers = []
170191
navigation.popHandler = nil
171192
}
172-
193+
173194
typealias UIViewControllerType = PopAwareUINavigationController
174195
}
175196

176197
class PopAwareUINavigationController: UINavigationController, UINavigationControllerDelegate {
177198
var popHandler: (() -> Void)?
178199
var stackSizeProvider: (() -> Int)?
179-
200+
180201
override func viewDidLoad() {
181202
super.viewDidLoad()
182203
delegate = self
183204
}
184-
205+
185206
func navigationController(_ navigationController: UINavigationController, didShow _: UIViewController, animated _: Bool) {
186207
if let stackSizeProvider = stackSizeProvider, stackSizeProvider() > navigationController.viewControllers.count {
187208
popHandler?()

Giffy/Features/Favorite/FavoriteView.swift

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ import CommonUI
1313
import ComposableArchitecture
1414

1515
struct FavoriteView: View {
16+
@Environment(\.dismiss) var pop
1617
let store: StoreOf<FavoriteReducer>
1718

1819
var body: some View {
1920
WithViewStore(store, observe: { $0 }) { viewStore in
20-
NavigationView {
21+
ZStack {
2122
ScrollView(.vertical, showsIndicators: false) {
2223
SearchField { query in
2324
viewStore.send(.fetch(request: query))
2425
}
2526
.padding(.horizontal, 16)
2627
.padding(.vertical, 20)
27-
28+
.padding(.top, 52)
29+
2830
if viewStore.state.list.isEmpty {
2931
FavoriteEmptyView()
3032
.padding(.top, 50)
@@ -54,42 +56,56 @@ struct FavoriteView: View {
5456
.animation(.easeInOut(duration: 0.2), value: viewStore.list.count)
5557
.navigationBarBackButtonHidden(false)
5658
.navigationBarTitleDisplayMode(.inline)
57-
.toolbar {
58-
ToolbarItem(placement: .topBarLeading) {
59-
IconButton(
60-
iconName: "chevron.left",
61-
tint: .blue,
62-
onClick: {
63-
viewStore.send(.didBackPressed)
64-
}
65-
)
66-
}
67-
68-
ToolbarItem(placement: .principal) {
69-
Text(key: .titleFavorite)
70-
.font(.bold, size: 16)
59+
.showDialog(
60+
shouldDismissOnTapOutside: true,
61+
isShowing: viewStore.binding(
62+
get: { $0.shareImage != nil },
63+
send: .showShare(nil)
64+
)
65+
) {
66+
ShareView(store: viewStore.share)
67+
}
68+
.onAppear {
69+
viewStore.send(.fetch())
70+
}
71+
.onReceive(viewStore.state.detailDisappear) { _ in
72+
viewStore.send(.fetch())
73+
}
74+
75+
VStack {
76+
FavoriteToolbar(title: Localizable.titleFavorite.tr()) {
77+
pop()
7178
}
79+
Spacer()
7280
}
7381
}
74-
.showDialog(
75-
shouldDismissOnTapOutside: true,
76-
isShowing: viewStore.binding(
77-
get: { $0.shareImage != nil },
78-
send: .showShare(nil)
79-
)
80-
) {
81-
ShareView(store: viewStore.share)
82-
}
83-
.onAppear {
84-
viewStore.send(.fetch())
85-
}
86-
.onReceive(viewStore.state.detailDisappear) { _ in
87-
viewStore.send(.fetch())
88-
}
8982
}
9083
}
9184
}
9285

86+
struct FavoriteToolbar: View {
87+
var title: String
88+
var onBackPressed: () -> Void
89+
90+
var body: some View {
91+
HStack {
92+
IconButton(
93+
iconName: "chevron.left",
94+
tint: .Theme.red,
95+
size: 26,
96+
onClick: onBackPressed
97+
)
98+
Spacer()
99+
Text(title)
100+
.font(.system(size: 16, weight: .bold))
101+
Spacer()
102+
Spacer().frame(width: 32)
103+
}
104+
.padding([.horizontal, .bottom], 8)
105+
.background(Blur(style: .prominent).edgesIgnoringSafeArea(.top))
106+
}
107+
}
108+
93109
#Preview {
94110
FavoriteView(store: Injection.resolve())
95111
}

Giffy/Features/Home/Button.swift

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,8 @@ struct RedirectButton: View {
3636
.foregroundColor(.Theme.yellow)
3737
.frame(width: 17, height: 17)
3838
.padding(.all, 17)
39-
.background(Color.Theme.background)
40-
.clipShape(.circle)
41-
.contentShape(.circle)
42-
}.buttonStyle(.plain)
39+
.background(Circle().fill(Color.Theme.background))
40+
}
4341
}
4442
}
4543

@@ -51,18 +49,17 @@ struct FavoriteButton: View {
5149
var onClick: () -> Void
5250

5351
var body: some View {
54-
Image(systemName: isFavorite ? "heart.fill" : "heart")
55-
.resizable()
56-
.foregroundColor(!isInverted ? Color.Theme.red : Color.white)
57-
.frame(width: size.width - margin, height: size.height - margin - 4)
58-
.background(
59-
(!isInverted ? Color.Theme.background : Color.Theme.red)
60-
.clipShape(Circle())
61-
.frame(width: size.width, height: size.height)
62-
)
63-
.onTapGesture {
64-
onClick()
65-
}
52+
Button(action: onClick) {
53+
Image(systemName: isFavorite ? "heart.fill" : "heart")
54+
.resizable()
55+
.foregroundColor(!isInverted ? Color.Theme.red : Color.white)
56+
.frame(width: size.width - margin, height: size.height - margin - 4)
57+
.background(
58+
(!isInverted ? Color.Theme.background : Color.Theme.red)
59+
.clipShape(.circle)
60+
.frame(width: size.width, height: size.height)
61+
)
62+
}
6663
}
6764

6865
func frame(width: CGFloat, height: CGFloat) -> Self {

Giffy/Features/Home/GiffyRow.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ struct GiffyRow: View {
4949
FavoriteButton(isFavorite: $isFavorite, size: .init(width: 40, height: 40)) {
5050
onFavorite(giphy)
5151
}
52-
.tapScaleEffect()
5352
.padding(.trailing, 10)
5453
}
5554

@@ -64,6 +63,7 @@ struct GiffyRow: View {
6463
.animation(.linear(duration: 0.2), value: isSelected)
6564
}
6665

66+
@ViewBuilder
6767
var footer: some View {
6868
HStack {
6969
if !giphy.title.isEmpty {
@@ -84,7 +84,6 @@ struct GiffyRow: View {
8484
RedirectButton(onClick: {
8585
onTapRow?(giphy)
8686
})
87-
.tapScaleEffect()
8887
.showGiffyMenu(
8988
URL(string: giphy.url),
9089
data: downloadedImage,

Giffy/Features/Home/HomeView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct HomeView: View {
7878
) {
7979
ShareView(store: viewStore.share)
8080
}
81-
.onChange(of: viewStore.shareImage) { image, _ in
81+
.onChange(of: viewStore.shareImage) { _, image in
8282
tabState.isShowShare = image != nil
8383
}
8484
.onAppear {

0 commit comments

Comments
 (0)