From c8e639c77c40a6caeb715afe91c0d7ea132c6720 Mon Sep 17 00:00:00 2001 From: noeyiz Date: Thu, 4 Apr 2024 16:39:38 +0900 Subject: [PATCH 1/5] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iBox/Sources/Web/WebView.swift | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift index 463e896..c3d99ee 100644 --- a/iBox/Sources/Web/WebView.swift +++ b/iBox/Sources/Web/WebView.swift @@ -107,19 +107,4 @@ extension WebView: WKNavigationDelegate { } } - // func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - // print("웹뷰 로딩 실패: \(error.localizedDescription)") - // } - // - // func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { - // print("웹뷰 프로비저널 네비게이션 실패: \(error.localizedDescription)") - // } - // - // func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { - // if let url = navigationAction.request.url { - // print("웹뷰가 리다이렉트 되는 URL: \(url.absoluteString)") - // } - // - // decisionHandler(.allow) - // } } From 89fd0cecacad9cbc99fd4d2fe2094e216b2c4111 Mon Sep 17 00:00:00 2001 From: noeyiz Date: Tue, 9 Apr 2024 00:49:33 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=EC=9B=B9=EB=B7=B0=20=EB=A6=AC?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=8B=9C=20=EC=A0=9C=EC=8A=A4=EC=B2=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxList/BoxListViewController.swift | 1 + iBox/Sources/Web/RefreshControl.swift | 32 +++++++++ iBox/Sources/Web/WebView.swift | 66 ++++++++++++++++--- iBox/Sources/Web/WebViewController.swift | 21 ++++++ 4 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 iBox/Sources/Web/RefreshControl.swift diff --git a/iBox/Sources/BoxList/BoxListViewController.swift b/iBox/Sources/BoxList/BoxListViewController.swift index da1fe42..6bbe115 100644 --- a/iBox/Sources/BoxList/BoxListViewController.swift +++ b/iBox/Sources/BoxList/BoxListViewController.swift @@ -151,6 +151,7 @@ extension BoxListViewController: BoxListViewDelegate { } else { // 캐시에 없는 경우, 새로운 viewController 인스턴스를 생성하고 캐시에 추가합니다. let viewController = WebViewController() + viewController.delegate = self viewController.selectedWebsite = url viewController.title = name WebCacheManager.shared.cacheData(forKey: id, viewController: viewController) diff --git a/iBox/Sources/Web/RefreshControl.swift b/iBox/Sources/Web/RefreshControl.swift new file mode 100644 index 0000000..011236f --- /dev/null +++ b/iBox/Sources/Web/RefreshControl.swift @@ -0,0 +1,32 @@ +// +// RefreshControl.swift +// iBox +// +// Created by jiyeon on 4/4/24. +// + +import UIKit + +class RefreshControl: UIView { + + // MARK: - UI Components + + + + // MARK: - Initializer + + override init(frame: CGRect) { + super.init(frame: frame) + setupProperty() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupProperty() { + backgroundColor = .systemBlue + isUserInteractionEnabled = true + } + +} diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift index c3d99ee..e8697af 100644 --- a/iBox/Sources/Web/WebView.swift +++ b/iBox/Sources/Web/WebView.swift @@ -12,6 +12,8 @@ import SnapKit class WebView: UIView { + var delegate: WebViewDelegate? + private var progressObserver: NSKeyValueObservation? var selectedWebsite: URL? { @@ -20,6 +22,9 @@ class WebView: UIView { } } + private var refreshControlHeight: CGFloat = 100.0 + private var isActive = false + // MARK: - UI Components private let webView = WKWebView().then { @@ -27,8 +32,6 @@ class WebView: UIView { $0.scrollView.contentInsetAdjustmentBehavior = .always } - private let refreshControl = UIRefreshControl() - private let progressView = UIProgressView().then { $0.progressViewStyle = .bar $0.tintColor = .label @@ -60,11 +63,21 @@ class WebView: UIView { private func setupProperty() { backgroundColor = .backgroundColor webView.navigationDelegate = self - webView.scrollView.refreshControl = refreshControl - refreshControl.addTarget(self, action: #selector(handleRefreshControl), for: .valueChanged) progressObserver = webView.observe(\.estimatedProgress, options: .new) { [weak self] webView, _ in self?.progressView.setProgress(Float(webView.estimatedProgress), animated: true) } + setupRefreshControl() + } + + private func setupRefreshControl() { + // pan gesture + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handleSwipe)) + panGestureRecognizer.delegate = self + addGestureRecognizer(panGestureRecognizer) + // refresh control + let refreshControl = RefreshControl(frame: .init(x: 0, y: -refreshControlHeight, width: frame.width, height: refreshControlHeight)) + webView.scrollView.addSubview(refreshControl) + webView.scrollView.delegate = self } private func setupHierarchy() { @@ -88,10 +101,25 @@ class WebView: UIView { webView.allowsBackForwardNavigationGestures = true } - @objc private func handleRefreshControl() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { [weak self] in - self?.webView.reload() - self?.refreshControl.endRefreshing() + @objc func handleSwipe(_ gesture: UIPanGestureRecognizer) { + guard isActive else { return } // 활성 상태일 때만 처리 + + let translation = gesture.translation(in: self) + if gesture.state == .ended { // 사용자의 터치가 끝났을 때 + if abs(translation.x) > 100 { + if translation.x > 0 { // 오른쪽 스와이프 : 처음 북마크로 돌아가기 + loadWebsite() + } else { // 왼쪽 스와이프 : 현재 링크 북마크 추가 + guard let url = webView.url else { return } + delegate?.pushAddBookMarkViewController(url: url) + } + } else { // 아래 : 새로고침 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.webView.reload() + } + } + // 제스처 인식 후 초기화 + gesture.setTranslation(CGPoint.zero, in: self) } } @@ -108,3 +136,25 @@ extension WebView: WKNavigationDelegate { } } + +extension WebView: UIScrollViewDelegate { + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + let refreshControlHeight: CGFloat = 100.0 + if scrollView.contentOffset.y < -refreshControlHeight { + isActive = true + } else { + isActive = false + } + } + +} + +extension WebView: UIGestureRecognizerDelegate { + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + // 다른 제스처 인식기와 동시에 인식되도록 허용 + return true + } + +} diff --git a/iBox/Sources/Web/WebViewController.swift b/iBox/Sources/Web/WebViewController.swift index ac000fd..d5741ce 100644 --- a/iBox/Sources/Web/WebViewController.swift +++ b/iBox/Sources/Web/WebViewController.swift @@ -7,8 +7,13 @@ import UIKit +protocol WebViewDelegate { + func pushAddBookMarkViewController(url: URL) +} + class WebViewController: BaseViewController, BaseViewControllerProtocol { + var delegate: AddBookmarkViewControllerProtocol? var selectedWebsite: URL? // MARK: - Life Cycle @@ -20,6 +25,7 @@ class WebViewController: BaseViewController, BaseViewControllerProtocol view.backgroundColor = .backgroundColor guard let contentView = contentView as? WebView else { return } + contentView.delegate = self contentView.selectedWebsite = selectedWebsite } @@ -30,3 +36,18 @@ class WebViewController: BaseViewController, BaseViewControllerProtocol } } + +extension WebViewController: WebViewDelegate { + + func pushAddBookMarkViewController(url: URL) { + URLDataManager.shared.incomingData = url.absoluteString + + let addBookmarkViewController = AddBookmarkViewController() + addBookmarkViewController.delegate = delegate + + let navigationController = UINavigationController(rootViewController: addBookmarkViewController) + navigationController.modalPresentationStyle = .pageSheet + self.present(navigationController, animated: true, completion: nil) + } + +} From fbcb4dbf61603dfdfdd23682650e282b0e52061f Mon Sep 17 00:00:00 2001 From: noeyiz Date: Tue, 9 Apr 2024 03:10:43 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Extension/UIColor+Extension.swift | 2 +- iBox/Sources/Extension/UIFont+Extension.swift | 1 + iBox/Sources/Web/RefreshControl.swift | 83 ++++++++++++++++++- iBox/Sources/Web/WebView.swift | 71 ++++++++++------ iBox/Sources/Web/WebViewController.swift | 6 ++ 5 files changed, 137 insertions(+), 26 deletions(-) diff --git a/iBox/Sources/Extension/UIColor+Extension.swift b/iBox/Sources/Extension/UIColor+Extension.swift index c0a3e69..2f30e8d 100644 --- a/iBox/Sources/Extension/UIColor+Extension.swift +++ b/iBox/Sources/Extension/UIColor+Extension.swift @@ -32,7 +32,7 @@ extension UIColor { static let box = UIColor(hex: 0xFF7F29) static let box2 = UIColor(hex: 0xFF9548) static let box3 = UIColor(hex: 0xFFDC6E) - static let tableViewBackgroundColor = color(light: .systemGroupedBackground, dark: .systemGray5) + static let tableViewBackgroundColor = color(light: .systemGroupedBackground, dark: .systemGray4) static let folderGray = color(light: .systemGray3, dark: .systemGray2) static let webIconColor = color(light: .black, dark: .systemGray) static let dimmedViewColor = UIColor.black.withAlphaComponent(0.75) diff --git a/iBox/Sources/Extension/UIFont+Extension.swift b/iBox/Sources/Extension/UIFont+Extension.swift index 7cf7a00..c222e95 100644 --- a/iBox/Sources/Extension/UIFont+Extension.swift +++ b/iBox/Sources/Extension/UIFont+Extension.swift @@ -17,5 +17,6 @@ extension UIFont { static let cellTitleFont = UIFont.systemFont(ofSize: 16.0) static let cellDescriptionFont = UIFont.systemFont(ofSize: 13.0, weight: .regular) static let descriptionFont = UIFont.systemFont(ofSize: 14.0) + static let refreshControlFont = UIFont.boldSystemFont(ofSize: 12.0) } diff --git a/iBox/Sources/Web/RefreshControl.swift b/iBox/Sources/Web/RefreshControl.swift index 011236f..8f791aa 100644 --- a/iBox/Sources/Web/RefreshControl.swift +++ b/iBox/Sources/Web/RefreshControl.swift @@ -7,10 +7,58 @@ import UIKit +import SnapKit + +enum RefreshControlType { + case addBookmark + case refresh + case back +} + class RefreshControl: UIView { + var currentType: RefreshControlType? + // MARK: - UI Components + let addBookmarkButton = UIButton().then { + $0.configuration = .plain() + $0.tintColor = .label + $0.configuration?.image = UIImage(systemName: "bookmark.circle") + $0.configuration?.imagePadding = 10 + $0.configuration?.imagePlacement = .top + $0.configuration?.preferredSymbolConfigurationForImage = .init(pointSize: 20.0) + $0.configuration?.attributedTitle = AttributedString("북마크 추가", attributes: AttributeContainer([NSAttributedString.Key.font: UIFont.refreshControlFont])) + $0.layer.cornerRadius = 15 + } + + let refreshButton = UIButton().then { + $0.configuration = .plain() + $0.tintColor = .label + $0.configuration?.image = UIImage(systemName: "arrow.clockwise.circle") + $0.configuration?.imagePadding = 10 + $0.configuration?.imagePlacement = .top + $0.configuration?.preferredSymbolConfigurationForImage = .init(pointSize: 20.0) + $0.configuration?.attributedTitle = AttributedString("새로고침", attributes: AttributeContainer([NSAttributedString.Key.font: UIFont.refreshControlFont])) + $0.layer.cornerRadius = 15 + } + + let backButton = UIButton().then { + $0.configuration = .plain() + $0.tintColor = .label + $0.configuration?.image = UIImage(systemName: "arrowshape.turn.up.backward.circle") + $0.configuration?.imagePadding = 10 + $0.configuration?.imagePlacement = .top + $0.configuration?.preferredSymbolConfigurationForImage = .init(pointSize: 20.0) + $0.configuration?.attributedTitle = AttributedString("처음으로 이동", attributes: AttributeContainer([NSAttributedString.Key.font: UIFont.refreshControlFont])) + $0.layer.cornerRadius = 15 + } + + let stackView = UIStackView().then { + $0.axis = .horizontal + $0.distribution = .fillEqually + $0.spacing = 20 + } // MARK: - Initializer @@ -18,15 +66,48 @@ class RefreshControl: UIView { override init(frame: CGRect) { super.init(frame: frame) setupProperty() + setupHierarchy() + setupLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + // MAKR: - Setup Methods + private func setupProperty() { - backgroundColor = .systemBlue isUserInteractionEnabled = true } + private func setupHierarchy() { + addSubview(stackView) + stackView.addArrangedSubview(addBookmarkButton) + stackView.addArrangedSubview(refreshButton) + stackView.addArrangedSubview(backButton) + } + + private func setupLayout() { + stackView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(20) + } + } + + func setSelected(_ type: RefreshControlType) { + if type == currentType { return } + currentType = type + clear() + switch type { + case .addBookmark: addBookmarkButton.backgroundColor = .tableViewBackgroundColor + case .refresh: refreshButton.backgroundColor = .tableViewBackgroundColor + case .back: backButton.backgroundColor = .tableViewBackgroundColor + } + } + + func clear() { + [addBookmarkButton, refreshButton, backButton].forEach { button in + button.backgroundColor = .clear + } + } + } diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift index e8697af..12c3cbd 100644 --- a/iBox/Sources/Web/WebView.swift +++ b/iBox/Sources/Web/WebView.swift @@ -22,7 +22,8 @@ class WebView: UIView { } } - private var refreshControlHeight: CGFloat = 100.0 + private var debounceTimer: Timer? + private var refreshControlHeight: CGFloat = 120.0 private var isActive = false // MARK: - UI Components @@ -38,6 +39,8 @@ class WebView: UIView { $0.sizeToFit() } + private var refreshControl: RefreshControl? + // MARK: - Initializer override init(frame: CGRect) { @@ -45,6 +48,7 @@ class WebView: UIView { setupProperty() setupHierarchy() setupLayout() +// setupRefreshControl() } required init?(coder: NSCoder) { @@ -66,18 +70,6 @@ class WebView: UIView { progressObserver = webView.observe(\.estimatedProgress, options: .new) { [weak self] webView, _ in self?.progressView.setProgress(Float(webView.estimatedProgress), animated: true) } - setupRefreshControl() - } - - private func setupRefreshControl() { - // pan gesture - let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handleSwipe)) - panGestureRecognizer.delegate = self - addGestureRecognizer(panGestureRecognizer) - // refresh control - let refreshControl = RefreshControl(frame: .init(x: 0, y: -refreshControlHeight, width: frame.width, height: refreshControlHeight)) - webView.scrollView.addSubview(refreshControl) - webView.scrollView.delegate = self } private func setupHierarchy() { @@ -95,31 +87,63 @@ class WebView: UIView { } } + func setupRefreshControl() { + // pan gesture + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handleSwipe)) + panGestureRecognizer.delegate = self // UIGestureRecognizerDelegate + addGestureRecognizer(panGestureRecognizer) + // refresh control + let refreshControl = RefreshControl(frame: .init(x: 0, y: -refreshControlHeight, width: frame.size.width, height: refreshControlHeight)) + webView.scrollView.addSubview(refreshControl) + webView.scrollView.delegate = self // UIScrollViewDelegate + self.refreshControl = refreshControl + } + private func loadWebsite() { guard let url = selectedWebsite else { return } webView.load(URLRequest(url: url)) webView.allowsBackForwardNavigationGestures = true } + private func debounce(interval: TimeInterval, action: @escaping (() -> Void)) { + debounceTimer?.invalidate() + debounceTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false, block: { _ in action() }) + } + @objc func handleSwipe(_ gesture: UIPanGestureRecognizer) { - guard isActive else { return } // 활성 상태일 때만 처리 + guard isActive, let refreshControl = refreshControl else { return } let translation = gesture.translation(in: self) - if gesture.state == .ended { // 사용자의 터치가 끝났을 때 - if abs(translation.x) > 100 { + if gesture.state == .changed { + if abs(translation.x) > refreshControlHeight { if translation.x > 0 { // 오른쪽 스와이프 : 처음 북마크로 돌아가기 - loadWebsite() + refreshControl.setSelected(.back) } else { // 왼쪽 스와이프 : 현재 링크 북마크 추가 - guard let url = webView.url else { return } - delegate?.pushAddBookMarkViewController(url: url) + refreshControl.setSelected(.addBookmark) } } else { // 아래 : 새로고침 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in - self?.webView.reload() - } + refreshControl.setSelected(.refresh) + } + } else if gesture.state == .ended { // 사용자의 터치가 끝났을 때 + switch refreshControl.currentType { + case .addBookmark: + guard let url = webView.url else { return } + delegate?.pushAddBookMarkViewController(url: url) + case .refresh: + self.webView.reload() + case .back: + loadWebsite() + case .none: break } - // 제스처 인식 후 초기화 + // 제스처 완료 후 초기화 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + refreshControl.clear() + } + } + // 제스처 초기화 + if gesture.state == .ended || gesture.state == .cancelled { gesture.setTranslation(CGPoint.zero, in: self) + refreshControl.currentType = nil } } @@ -140,7 +164,6 @@ extension WebView: WKNavigationDelegate { extension WebView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { - let refreshControlHeight: CGFloat = 100.0 if scrollView.contentOffset.y < -refreshControlHeight { isActive = true } else { diff --git a/iBox/Sources/Web/WebViewController.swift b/iBox/Sources/Web/WebViewController.swift index d5741ce..19b2ebe 100644 --- a/iBox/Sources/Web/WebViewController.swift +++ b/iBox/Sources/Web/WebViewController.swift @@ -29,6 +29,12 @@ class WebViewController: BaseViewController, BaseViewControllerProtocol contentView.selectedWebsite = selectedWebsite } + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + guard let contentView = contentView as? WebView else { return } + contentView.setupRefreshControl() + } + // MARK: - BaseViewControllerProtocol func setupNavigationBar() { From 5e3e22982ddbd9b2b22be0d9687c526878945aab Mon Sep 17 00:00:00 2001 From: noeyiz Date: Tue, 9 Apr 2024 14:03:24 +0900 Subject: [PATCH 4/5] =?UTF-8?q?style:=20RefreshControl=20backgroundColor?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iBox/Sources/Web/RefreshControl.swift | 1 + iBox/Sources/Web/WebView.swift | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/iBox/Sources/Web/RefreshControl.swift b/iBox/Sources/Web/RefreshControl.swift index 8f791aa..874139f 100644 --- a/iBox/Sources/Web/RefreshControl.swift +++ b/iBox/Sources/Web/RefreshControl.swift @@ -77,6 +77,7 @@ class RefreshControl: UIView { // MAKR: - Setup Methods private func setupProperty() { + backgroundColor = .backgroundColor isUserInteractionEnabled = true } diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift index 12c3cbd..6d25a2c 100644 --- a/iBox/Sources/Web/WebView.swift +++ b/iBox/Sources/Web/WebView.swift @@ -48,7 +48,6 @@ class WebView: UIView { setupProperty() setupHierarchy() setupLayout() -// setupRefreshControl() } required init?(coder: NSCoder) { From e16b77f4657126d2c33a8729e9fad9ac4f213366 Mon Sep 17 00:00:00 2001 From: noeyiz Date: Tue, 9 Apr 2024 14:46:21 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20RefreshControl=20=EB=86=92=EC=9D=B4?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EC=A7=84=EB=8F=99=20?= =?UTF-8?q?=ED=9A=A8=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iBox/Sources/Web/RefreshControl.swift | 7 ++++++- iBox/Sources/Web/WebView.swift | 11 +++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/iBox/Sources/Web/RefreshControl.swift b/iBox/Sources/Web/RefreshControl.swift index 874139f..6f1a80f 100644 --- a/iBox/Sources/Web/RefreshControl.swift +++ b/iBox/Sources/Web/RefreshControl.swift @@ -90,7 +90,7 @@ class RefreshControl: UIView { private func setupLayout() { stackView.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(20) + make.leading.bottom.trailing.equalToSuperview().inset(20) } } @@ -103,6 +103,11 @@ class RefreshControl: UIView { case .refresh: refreshButton.backgroundColor = .tableViewBackgroundColor case .back: backButton.backgroundColor = .tableViewBackgroundColor } + if UserDefaultsManager.isHaptics { + let generator = UIImpactFeedbackGenerator(style: .light) + generator.prepare() + generator.impactOccurred() + } } func clear() { diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift index 6d25a2c..fd7f2ed 100644 --- a/iBox/Sources/Web/WebView.swift +++ b/iBox/Sources/Web/WebView.swift @@ -22,7 +22,6 @@ class WebView: UIView { } } - private var debounceTimer: Timer? private var refreshControlHeight: CGFloat = 120.0 private var isActive = false @@ -92,8 +91,9 @@ class WebView: UIView { panGestureRecognizer.delegate = self // UIGestureRecognizerDelegate addGestureRecognizer(panGestureRecognizer) // refresh control - let refreshControl = RefreshControl(frame: .init(x: 0, y: -refreshControlHeight, width: frame.size.width, height: refreshControlHeight)) + let refreshControl = RefreshControl(frame: .init(x: 0, y: -frame.size.height, width: frame.size.width, height: frame.size.height)) webView.scrollView.addSubview(refreshControl) + webView.scrollView.backgroundColor = .backgroundColor webView.scrollView.delegate = self // UIScrollViewDelegate self.refreshControl = refreshControl } @@ -104,17 +104,12 @@ class WebView: UIView { webView.allowsBackForwardNavigationGestures = true } - private func debounce(interval: TimeInterval, action: @escaping (() -> Void)) { - debounceTimer?.invalidate() - debounceTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false, block: { _ in action() }) - } - @objc func handleSwipe(_ gesture: UIPanGestureRecognizer) { guard isActive, let refreshControl = refreshControl else { return } let translation = gesture.translation(in: self) if gesture.state == .changed { - if abs(translation.x) > refreshControlHeight { + if abs(translation.x) > 60.0 { if translation.x > 0 { // 오른쪽 스와이프 : 처음 북마크로 돌아가기 refreshControl.setSelected(.back) } else { // 왼쪽 스와이프 : 현재 링크 북마크 추가