Skip to content

Commit

Permalink
Merge pull request openedx#389 from edx/feat/back-navigation-popup
Browse files Browse the repository at this point in the history
[iOS] Add long tap and menu for custom navbar
  • Loading branch information
forgotvas authored Apr 15, 2024
2 parents a9c562d + 955a8e3 commit 0858f81
Show file tree
Hide file tree
Showing 19 changed files with 249 additions and 46 deletions.
13 changes: 7 additions & 6 deletions Authorization/Authorization/Presentation/Login/SignInView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ public struct SignInView: View {
}.frame(maxWidth: .infinity, maxHeight: 200)
if viewModel.config.features.startupScreenEnabled {
VStack {
Button(action: { viewModel.router.back() }, label: {
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.backButtonStyle(color: Theme.Colors.loginNavigationText)
})
.foregroundColor(Theme.Colors.styledButtonText)
BackNavigationButton(
color: Theme.Colors.loginNavigationText,
action: {
viewModel.router.back()
}
)
.backViewStyle()
.padding(.leading, isHorizontal ? 48 : 0)
.padding(.top, 11)
.accessibilityIdentifier("back_button")

}.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(.top, isHorizontal ? 20 : 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ public struct SignUpView: View {
.accessibilityIdentifier("register_text")
}
VStack {
Button(action: { viewModel.router.back() }, label: {
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.backButtonStyle(color: Theme.Colors.loginNavigationText)
})
.foregroundColor(Theme.Colors.styledButtonText)
BackNavigationButton(
color: Theme.Colors.loginNavigationText,
action: {
viewModel.router.back()
}
)
.backViewStyle()
.padding(.leading, isHorizontal ? 48 : 0)
.accessibilityIdentifier("back_button")

}.frame(minWidth: 0,
maxWidth: .infinity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public struct StartupView: View {
}
.frameLimit()
}
.navigationTitle(AuthLocalization.Startup.title)
.hideNavigationBar()
.padding(.all, isHorizontal ? 1 : 0)
.background(Theme.Colors.background.ignoresSafeArea(.all))
Expand Down
2 changes: 2 additions & 0 deletions Authorization/Authorization/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public enum AuthLocalization {
public static let searchPlaceholder = AuthLocalization.tr("Localizable", "STARTUP.SEARCH_PLACEHOLDER", fallback: "Search our 3000+ courses")
/// What do you want to learn?
public static let searchTitle = AuthLocalization.tr("Localizable", "STARTUP.SEARCH_TITLE", fallback: "What do you want to learn?")
/// Start
public static let title = AuthLocalization.tr("Localizable", "STARTUP.TITLE", fallback: "Start")
}
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
Expand Down
1 change: 1 addition & 0 deletions Authorization/Authorization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ accordance with the [Privacy Policy.](%@)";
"STARTUP.SEARCH_TITLE" = "What do you want to learn?";
"STARTUP.SEARCH_PLACEHOLDER" = "Search our 3000+ courses";
"STARTUP.EXPLORE_ALL_COURSES" = "Explore all courses";
"STARTUP.TITLE" = "Start";
1 change: 1 addition & 0 deletions Authorization/Authorization/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ accordance with the [Privacy Policy.](%@)";
"STARTUP.SEARCH_TITLE" = "What do you want to learn?";
"STARTUP.SEARCH_PLACEHOLDER" = "Search our 3000+ courses";
"STARTUP.EXPLORE_ALL_COURSES" = "Explore all courses";
"STARTUP.TITLE" = "Start";
8 changes: 8 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
06619EAD2B90918B001FAADE /* ReadabilityInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06619EAC2B90918B001FAADE /* ReadabilityInjection.swift */; };
06619EAF2B973B25001FAADE /* AccessibilityInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06619EAE2B973B25001FAADE /* AccessibilityInjection.swift */; };
06BEEA0E2B6A55C500D25A97 /* ColorInversionInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BEEA0D2B6A55C500D25A97 /* ColorInversionInjection.swift */; };
06DEA4A32BBD66A700110D20 /* BackNavigationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06DEA4A22BBD66A700110D20 /* BackNavigationButton.swift */; };
06DEA4A52BBD66D700110D20 /* BackNavigationButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06DEA4A42BBD66D700110D20 /* BackNavigationButtonViewModel.swift */; };
070019A528F6F17900D5FC78 /* Data_Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019A428F6F17900D5FC78 /* Data_Media.swift */; };
070019AC28F6FD0100D5FC78 /* CourseDetailBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */; };
070019AE28F701B200D5FC78 /* Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AD28F701B200D5FC78 /* Certificate.swift */; };
Expand Down Expand Up @@ -275,6 +277,8 @@
06619EAC2B90918B001FAADE /* ReadabilityInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadabilityInjection.swift; sourceTree = "<group>"; };
06619EAE2B973B25001FAADE /* AccessibilityInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityInjection.swift; sourceTree = "<group>"; };
06BEEA0D2B6A55C500D25A97 /* ColorInversionInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorInversionInjection.swift; sourceTree = "<group>"; };
06DEA4A22BBD66A700110D20 /* BackNavigationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackNavigationButton.swift; sourceTree = "<group>"; };
06DEA4A42BBD66D700110D20 /* BackNavigationButtonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackNavigationButtonViewModel.swift; sourceTree = "<group>"; };
070019A428F6F17900D5FC78 /* Data_Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Media.swift; sourceTree = "<group>"; };
070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDetailBlock.swift; sourceTree = "<group>"; };
070019AD28F701B200D5FC78 /* Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Certificate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -725,6 +729,8 @@
BA8FA6672AD59A5700EA029A /* SocialAuthButton.swift */,
02E93F862AEBAED4006C4750 /* AppReview */,
BA981BCF2B91ED50005707C2 /* FullScreenProgressView.swift */,
06DEA4A22BBD66A700110D20 /* BackNavigationButton.swift */,
06DEA4A42BBD66D700110D20 /* BackNavigationButtonViewModel.swift */,
);
path = Base;
sourceTree = "<group>";
Expand Down Expand Up @@ -1062,6 +1068,7 @@
06619EAF2B973B25001FAADE /* AccessibilityInjection.swift in Sources */,
BAFB99822B0E2354007D09F9 /* FacebookConfig.swift in Sources */,
027BD3B32909475900392132 /* Publishers+KeyboardState.swift in Sources */,
06DEA4A32BBD66A700110D20 /* BackNavigationButton.swift in Sources */,
0727877D28D25212002E9142 /* ProgressBar.swift in Sources */,
BA981BD02B91ED50005707C2 /* FullScreenProgressView.swift in Sources */,
0236961F28F9A2F600EEF206 /* AuthEndpoint.swift in Sources */,
Expand Down Expand Up @@ -1142,6 +1149,7 @@
072787B628D37A0E002E9142 /* Validator.swift in Sources */,
0236961D28F9A2D200EEF206 /* Data_AuthResponse.swift in Sources */,
A5F4E7B52B61544A00ACD166 /* BrazeConfig.swift in Sources */,
06DEA4A52BBD66D700110D20 /* BackNavigationButtonViewModel.swift in Sources */,
02AFCC182AEFDB24000360F0 /* ThirdPartyMailClient.swift in Sources */,
0233D5712AF13EC800BAC8BD /* SelectMailClientView.swift in Sources */,
BAFB99842B0E282E007D09F9 /* MicrosoftConfig.swift in Sources */,
Expand Down
11 changes: 8 additions & 3 deletions Core/Core/Extensions/ViewExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ public extension View {
func onFirstAppear(_ action: @escaping () -> Void) -> some View {
modifier(FirstAppear(action: action))
}

func backViewStyle(topPadding: CGFloat = -10) -> some View {
return self
.frame(height: 24)
.padding(.horizontal, 8)
.offset(y: topPadding)
}
}

public extension View {
Expand Down Expand Up @@ -300,10 +307,8 @@ public extension Image {
.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(height: 24)
.padding(.horizontal, 8)
.offset(y: topPadding)
.foregroundColor(color)
.backViewStyle(topPadding: topPadding)
}
}

Expand Down
93 changes: 93 additions & 0 deletions Core/Core/View/Base/BackNavigationButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// BackNavigationButton.swift
// Core
//
// Created by Vadim Kuznetsov on 3.04.24.
//

import SwiftUI
import Theme

class BackButton: UIButton {
override func menuAttachmentPoint(for configuration: UIContextMenuConfiguration) -> CGPoint {
return .zero
}
}

public struct BackNavigationButtonRepresentable: UIViewRepresentable {
@ObservedObject var viewModel: BackNavigationButtonViewModel
var action: (() -> Void)?
var color: Color

init(action: (() -> Void)? = nil, color: Color, viewModel: BackNavigationButtonViewModel) {
self.viewModel = viewModel
self.action = action
self.color = color
}

public func makeUIView(context: Context) -> UIButton {
let button = BackButton(type: .custom)
let image = CoreAssets.arrowLeft.image.withRenderingMode(.alwaysTemplate)
button.setImage(image, for: .normal)
button.tintColor = UIColor(color)
button.contentHorizontalAlignment = .leading
button.addTarget(context.coordinator, action: #selector(Coordinator.buttonAction), for: .touchUpInside)
button.accessibilityIdentifier = "back_button"
return button
}

public func updateUIView(_ button: UIButton, context: Context) {
var actions: [UIAction] = []
for item in viewModel.items {
let action = UIAction(title: item.title) {[weak viewModel] _ in
viewModel?.navigateTo(item: item)
}
actions.append(action)
}
button.menu = UIMenu(title: "", children: actions)
}

public func makeCoordinator() -> Coordinator {
Coordinator(action: action)
}

public class Coordinator: NSObject {
var action: (() -> Void)?
init(action: (() -> Void)?) {
self.action = action
}

@objc func buttonAction() {
action?()
}
}
}

public struct BackNavigationButton: View {
@StateObject var viewModel = BackNavigationButtonViewModel()
private let color: Color
private let action: (() -> Void)?

public init(
color: Color = Theme.Colors.accentXColor,
action: (() -> Void)? = nil
) {
self.color = color
self.action = action
}

public var body: some View {
BackNavigationButtonRepresentable(action: action, color: color, viewModel: viewModel)
.onAppear {
viewModel.loadItems()
}

}
}
#if DEBUG
struct BackNavigationButton_Previews: PreviewProvider {
static var previews: some View {
BackNavigationButton()
}
}
#endif
41 changes: 41 additions & 0 deletions Core/Core/View/Base/BackNavigationButtonViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// BackNavigationButtonViewModel.swift
// Core
//
// Created by Vadim Kuznetsov on 3.04.24.
//

import Swinject
import UIKit

public protocol BackNavigationProtocol {
func getBackMenuItems() -> [BackNavigationMenuItem]
func navigateTo(item: BackNavigationMenuItem)
}

public struct BackNavigationMenuItem: Identifiable {
public var id: Int
public var title: String

public init(id: Int, title: String) {
self.id = id
self.title = title
}
}

class BackNavigationButtonViewModel: ObservableObject {
private let helper: BackNavigationProtocol
@Published var items: [BackNavigationMenuItem] = []

init() {
self.helper = Container.shared.resolve(BackNavigationProtocol.self)!
}

func loadItems() {
self.items = helper.getBackMenuItems()
}

func navigateTo(item: BackNavigationMenuItem) {
helper.navigateTo(item: item)
}
}
22 changes: 8 additions & 14 deletions Core/Core/View/Base/NavigationBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,14 @@ public struct NavigationBar: View {
}
.padding(.horizontal, 24)
if leftButton {
VStack {
Button(action: {
leftButtonAction?()
}, label: {
CoreAssets.arrowLeft.swiftUIImage
.backButtonStyle(color: leftButtonColor)
.padding(8)
})
.foregroundColor(Theme.Colors.styledButtonText)
.accessibilityIdentifier("back_button")

}.frame(minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading)
VStack {
BackNavigationButton(color: leftButtonColor, action: leftButtonAction)
.padding(8)
.backViewStyle()
}
.frame(minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading)

}
if rightButtonType != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public struct SearchView: View {
viewModel.searchText = ""
}
.background(Theme.Colors.background.ignoresSafeArea())
.addTapToEndEditing(isForced: true)
.avoidKeyboard(dismissKeyboardByTap: true)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,19 @@ public struct ResponsesView: View {
}
.ignoresSafeArea(.all, edges: .horizontal)
.navigationBarHidden(false)
.navigationBarBackButtonHidden(false)
.navigationBarBackButtonHidden(true)
.navigationTitle(title)
.toolbar {
ToolbarItem(
placement: .navigationBarLeading,
content: {
BackNavigationButton(color: Theme.Colors.accentColor) {
viewModel.router.back()
}
.offset(x: -8, y: -1.5)
}
)
}
.edgesIgnoringSafeArea(.bottom)
.background(
Theme.Colors.background
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,19 @@ public struct ThreadView: View {
}
.ignoresSafeArea(.all, edges: .horizontal)
.navigationBarHidden(false)
.navigationBarBackButtonHidden(false)
.navigationBarBackButtonHidden(true)
.navigationTitle(title)
.toolbar {
ToolbarItem(
placement: .navigationBarLeading,
content: {
BackNavigationButton(color: Theme.Colors.accentColor) {
viewModel.router.back()
}
.offset(x: -8, y: -1.5)
}
)
}
.onFirstAppear {
Task {
await viewModel.getPosts(thread: thread, page: 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,18 @@ public struct DiscussionSearchTopicsView: View {
}
}
}
.background(Theme.Colors.background.ignoresSafeArea())
.avoidKeyboard(dismissKeyboardByTap: true)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.navigationTitle(DiscussionLocalization.search)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now()) {
withAnimation(.easeIn(duration: 0.3)) {
animated = true
}
}
}
.background(Theme.Colors.background.ignoresSafeArea())
.addTapToEndEditing(isForced: true)
}
}

Expand Down
4 changes: 4 additions & 0 deletions OpenEdX/DI/ScreenAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ class ScreenAssembly: Assembly {
config: r.resolve(ConfigProtocol.self)!
)
}

container.register(BackNavigationProtocol.self) { r in
r.resolve(Router.self)!
}
}
}
// swiftlint:enable function_body_length type_body_length
Loading

0 comments on commit 0858f81

Please sign in to comment.