Animate reader view button
This commit is contained in:
parent
eb69967899
commit
98befac78c
|
@ -57,7 +57,7 @@ class ArticleExtractorButton: NSButton {
|
|||
case isError:
|
||||
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractorError, opacity: opacity)
|
||||
case isInProgress:
|
||||
addProgressSublayer(to: hostedLayer)
|
||||
addAnimatedSublayer(to: hostedLayer)
|
||||
default:
|
||||
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractor, opacity: opacity)
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class ArticleExtractorButton: NSButton {
|
|||
hostedLayer.addSublayer(imageLayer)
|
||||
}
|
||||
|
||||
private func addProgressSublayer(to hostedLayer: CALayer) {
|
||||
private func addAnimatedSublayer(to hostedLayer: CALayer) {
|
||||
let imageProgress1 = AppAssets.articleExtractorProgress1
|
||||
let imageProgress2 = AppAssets.articleExtractorProgress2
|
||||
let imageProgress3 = AppAssets.articleExtractorProgress3
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
|
||||
|
@ -777,6 +778,7 @@
|
|||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
|
||||
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
|
@ -1377,6 +1379,7 @@
|
|||
children = (
|
||||
51C4527E2265092C00C03939 /* ArticleViewController.swift */,
|
||||
517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */,
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */,
|
||||
);
|
||||
path = Article;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2823,6 +2826,7 @@
|
|||
51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */,
|
||||
51C452AF2265108300C03939 /* ArticleArray.swift in Sources */,
|
||||
51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */,
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */,
|
||||
84CAFCA522BC8C08007694F0 /* FetchRequestQueue.swift in Sources */,
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
|
||||
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
||||
|
|
|
@ -10,6 +10,28 @@ import RSCore
|
|||
|
||||
struct AppAssets {
|
||||
|
||||
static var articleExtractorError: UIImage = {
|
||||
return UIImage(named: "articleExtractorOff")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOff: UIImage = {
|
||||
return UIImage(named: "articleExtractorOff")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOffTinted: UIImage = {
|
||||
let image = UIImage(named: "articleExtractorOff")!
|
||||
return image.maskWithColor(color: AppAssets.primaryAccentColor.cgColor)!
|
||||
}()
|
||||
|
||||
static var articleExtractorOn: UIImage = {
|
||||
return UIImage(named: "articleExtractorOn")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOnTinted: UIImage = {
|
||||
let image = UIImage(named: "articleExtractorOn")!
|
||||
return image.maskWithColor(color: AppAssets.primaryAccentColor.cgColor)!
|
||||
}()
|
||||
|
||||
static var avatarBackgroundColor: UIColor = {
|
||||
return UIColor(named: "avatarBackgroundColor")!
|
||||
}()
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// ArticleExtractorButton.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/24/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum ArticleExtractorButtonState {
|
||||
case error
|
||||
case animated
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
class ArticleExtractorButton: UIButton {
|
||||
|
||||
var buttonState: ArticleExtractorButtonState = .off {
|
||||
didSet {
|
||||
if buttonState != oldValue {
|
||||
switch buttonState {
|
||||
case .error:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorError, for: .normal)
|
||||
case .animated:
|
||||
setImage(nil, for: .normal)
|
||||
setNeedsLayout()
|
||||
case .on:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorOn, for: .normal)
|
||||
case .off:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorOff, for: .normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
guard case .animated = buttonState else {
|
||||
return
|
||||
}
|
||||
stripSublayer()
|
||||
addAnimatedSublayer(to: layer)
|
||||
}
|
||||
|
||||
private func stripSublayer() {
|
||||
if layer.sublayers?.count ?? 0 > 1 {
|
||||
layer.sublayers?.last?.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
private func addAnimatedSublayer(to hostedLayer: CALayer) {
|
||||
let image1 = AppAssets.articleExtractorOffTinted.cgImage!
|
||||
let image2 = AppAssets.articleExtractorOnTinted.cgImage!
|
||||
let images = [image1, image2, image1]
|
||||
|
||||
let imageLayer = CALayer()
|
||||
let imageSize = AppAssets.articleExtractorOff.size
|
||||
imageLayer.bounds = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
|
||||
imageLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
|
||||
hostedLayer.addSublayer(imageLayer)
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.calculationMode = CAAnimationCalculationMode.linear
|
||||
animation.keyTimes = [0, 0.5, 1]
|
||||
animation.duration = 2
|
||||
animation.values = images as [Any]
|
||||
animation.repeatCount = HUGE
|
||||
|
||||
imageLayer.add(animation, forKey: "contents")
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,7 @@ enum ArticleViewState: Equatable {
|
|||
|
||||
class ArticleViewController: UIViewController {
|
||||
|
||||
@IBOutlet private weak var readerViewBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var articleExtractorButton: ArticleExtractorButton!
|
||||
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
|
||||
|
@ -55,6 +55,15 @@ class ArticleViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
var articleExtractorButtonState: ArticleExtractorButtonState {
|
||||
get {
|
||||
return articleExtractorButton.buttonState
|
||||
}
|
||||
set {
|
||||
articleExtractorButton.buttonState = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private let keyboardManager = KeyboardManager(type: .detail)
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
return keyboardManager.keyCommands
|
||||
|
@ -74,6 +83,9 @@ class ArticleViewController: UIViewController {
|
|||
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
|
||||
// For some reason interface builder won't let me set this there.
|
||||
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
|
||||
|
||||
ArticleViewControllerWebViewProvider.shared.dequeueWebView() { webView in
|
||||
|
||||
self.webView = webView
|
||||
|
@ -96,6 +108,7 @@ class ArticleViewController: UIViewController {
|
|||
func updateUI() {
|
||||
|
||||
guard let article = currentArticle else {
|
||||
articleExtractorButton.isEnabled = false
|
||||
nextUnreadBarButtonItem.isEnabled = false
|
||||
prevArticleBarButtonItem.isEnabled = false
|
||||
nextArticleBarButtonItem.isEnabled = false
|
||||
|
@ -110,6 +123,7 @@ class ArticleViewController: UIViewController {
|
|||
prevArticleBarButtonItem.isEnabled = coordinator.isPrevArticleAvailable
|
||||
nextArticleBarButtonItem.isEnabled = coordinator.isNextArticleAvailable
|
||||
|
||||
articleExtractorButton.isEnabled = true
|
||||
readBarButtonItem.isEnabled = true
|
||||
starBarButtonItem.isEnabled = true
|
||||
browserBarButtonItem.isEnabled = true
|
||||
|
@ -179,7 +193,7 @@ class ArticleViewController: UIViewController {
|
|||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func toggleReaderView(_ sender: Any) {
|
||||
@IBAction func toggleArticleExtractor(_ sender: Any) {
|
||||
coordinator.toggleArticleExtractor()
|
||||
}
|
||||
|
||||
|
|
|
@ -99,25 +99,24 @@
|
|||
</barButtonItem>
|
||||
</toolbarItems>
|
||||
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM">
|
||||
<barButtonItem key="rightBarButtonItem" title="Reader View" image="doc.plaintext" catalog="system" id="Bl3-sa-n84">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Reader View"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="toggleReaderView:" destination="JEX-9P-axG" id="o5N-Hy-Ma3"/>
|
||||
</connections>
|
||||
<barButtonItem key="rightBarButtonItem" style="plain" id="n8q-YO-ldL">
|
||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="n73-9B-rav" customClass="ArticleExtractorButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="362" y="5" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" image="articleExtractorOff"/>
|
||||
</button>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
||||
<outlet property="articleExtractorButton" destination="n73-9B-rav" id="zud-XU-Kkx"/>
|
||||
<outlet property="browserBarButtonItem" destination="DMh-3X-ebd" id="PkT-Tn-8kG"/>
|
||||
<outlet property="nextArticleBarButtonItem" destination="2qz-M5-Yhk" id="IQd-jx-qEr"/>
|
||||
<outlet property="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="xJr-5y-p1N"/>
|
||||
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
||||
<outlet property="readBarButtonItem" destination="hy0-LS-MzE" id="BzM-x9-tuj"/>
|
||||
<outlet property="readerViewBarButtonItem" destination="Bl3-sa-n84" id="JhM-7c-nIf"/>
|
||||
<outlet property="starBarButtonItem" destination="wU4-eH-wC9" id="Z8Q-Lt-dKk"/>
|
||||
<outlet property="webViewContainer" destination="DNb-lt-KzC" id="Fc1-Ae-pWK"/>
|
||||
</connections>
|
||||
|
@ -240,8 +239,8 @@
|
|||
<image name="arrow.down" catalog="system" width="58" height="64"/>
|
||||
<image name="arrow.down.circle" catalog="system" width="64" height="62"/>
|
||||
<image name="arrow.up" catalog="system" width="58" height="64"/>
|
||||
<image name="articleExtractorOff" width="18" height="23"/>
|
||||
<image name="circle" catalog="system" width="64" height="62"/>
|
||||
<image name="doc.plaintext" catalog="system" width="56" height="64"/>
|
||||
<image name="gear" catalog="system" width="64" height="60"/>
|
||||
<image name="safari" catalog="system" width="64" height="62"/>
|
||||
<image name="square.and.arrow.up" catalog="system" width="56" height="64"/>
|
||||
|
|
BIN
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/ArticleExtractorOff.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/ArticleExtractorOff.pdf
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOff.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
BIN
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/ArticleExtractorOff.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/ArticleExtractorOff.pdf
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOff.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
BIN
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/ArticleExtractorOn.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/ArticleExtractorOn.pdf
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOn.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
|
@ -559,7 +559,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||
articleExtractor?.cancel()
|
||||
articleExtractor = nil
|
||||
isShowingExtractedArticle = false
|
||||
// makeToolbarValidate()
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
|
||||
currentArticle = article
|
||||
activityManager.reading(currentArticle)
|
||||
|
@ -813,12 +813,14 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||
articleExtractor?.cancel()
|
||||
articleExtractor = nil
|
||||
isShowingExtractedArticle = false
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
articleViewController?.state = .article(article)
|
||||
return
|
||||
}
|
||||
|
||||
guard !isShowingExtractedArticle else {
|
||||
isShowingExtractedArticle = false
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
articleViewController?.state = .article(article)
|
||||
return
|
||||
}
|
||||
|
@ -826,6 +828,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||
if let articleExtractor = articleExtractor, let extractedArticle = articleExtractor.article {
|
||||
if currentArticle?.preferredLink == articleExtractor.articleLink {
|
||||
isShowingExtractedArticle = true
|
||||
articleViewController?.articleExtractorButtonState = .on
|
||||
articleViewController?.state = .extracted(article, extractedArticle)
|
||||
}
|
||||
} else {
|
||||
|
@ -924,14 +927,14 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
|||
extension SceneCoordinator: ArticleExtractorDelegate {
|
||||
|
||||
func articleExtractionDidFail(with: Error) {
|
||||
// makeToolbarValidate()
|
||||
articleViewController?.articleExtractorButtonState = .error
|
||||
}
|
||||
|
||||
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
|
||||
if let article = currentArticle, articleExtractor?.state != .cancelled {
|
||||
isShowingExtractedArticle = true
|
||||
articleViewController?.state = .extracted(article, extractedArticle)
|
||||
// makeToolbarValidate()
|
||||
articleViewController?.articleExtractorButtonState = .on
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1243,6 +1246,7 @@ private extension SceneCoordinator {
|
|||
extractor.delegate = self
|
||||
extractor.process()
|
||||
articleExtractor = extractor
|
||||
articleViewController?.articleExtractorButtonState = .animated
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue