diff --git a/.gitmodules b/.gitmodules index 250b3d0..4daf224 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "Frameworks/FLAnimatedImage"] path = Frameworks/FLAnimatedImage url = https://github.com/Flipboard/FLAnimatedImage.git +[submodule "Frameworks/Kingfisher"] + path = Frameworks/Kingfisher + url = https://github.com/onevcat/Kingfisher.git diff --git a/Frameworks/Kingfisher b/Frameworks/Kingfisher new file mode 160000 index 0000000..fbf8067 --- /dev/null +++ b/Frameworks/Kingfisher @@ -0,0 +1 @@ +Subproject commit fbf8067218a358b470baad99a1ed3836365540f5 diff --git a/elpha-ios.xcodeproj/project.pbxproj b/elpha-ios.xcodeproj/project.pbxproj index 053d252..2d19053 100644 --- a/elpha-ios.xcodeproj/project.pbxproj +++ b/elpha-ios.xcodeproj/project.pbxproj @@ -21,12 +21,13 @@ 152FB0FA218ADDD0001D6574 /* AttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FB0F9218ADDD0001D6574 /* AttachmentViewController.swift */; }; 152FBCD2219682E80079B3E8 /* AbstractStatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FBCD1219682E80079B3E8 /* AbstractStatusTableViewController.swift */; }; 152FBCE621978C4A0079B3E8 /* CellHeightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FBCE521978C4A0079B3E8 /* CellHeightManager.swift */; }; - 152FBCED219799FC0079B3E8 /* FLAnimatedImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */; }; - 152FBCEE219799FC0079B3E8 /* FLAnimatedImage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 152FBCF2219818AD0079B3E8 /* FieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FBCF1219818AD0079B3E8 /* FieldTableViewCell.swift */; }; + 15341866219FF29D002F5F8A /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15341865219FF29D002F5F8A /* SettingsManager.swift */; }; + 15341880219FF40F002F5F8A /* UIImageView+Effects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1534187F219FF40F002F5F8A /* UIImageView+Effects.swift */; }; 1539509121894A38009BA6E7 /* AlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1539509021894A38009BA6E7 /* AlertManager.swift */; }; + 156370BD219FE24200D51D42 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 156370B0219FE22E00D51D42 /* Kingfisher.framework */; }; + 156370BE219FE24200D51D42 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 156370B0219FE22E00D51D42 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 156902A0219A7D75002BF61F /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1569029F219A7D75002BF61F /* ComposeViewController.swift */; }; - 156902B3219A8A1C002BF61F /* FLAnimatedImageView+SetImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156902B2219A8A1C002BF61F /* FLAnimatedImageView+SetImage.swift */; }; 156FF015217289380074D9CA /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156FF014217289380074D9CA /* AccountTableViewCell.swift */; }; 156FF0312174797E0074D9CA /* StatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156FF0302174797E0074D9CA /* StatusTableViewController.swift */; }; 156FF04F2175CDBC0074D9CA /* MainStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156FF04E2175CDBC0074D9CA /* MainStatusTableViewCell.swift */; }; @@ -154,19 +155,61 @@ remoteGlobalIDString = 4CB928431C66E1A700CE5F08; remoteInfo = AlamofireNetworkActivityIndicatorTests; }; - 152FBCEB219799E50079B3E8 /* PBXContainerItemProxy */ = { + 156370AF219FE22E00D51D42 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 92C9BC0C1B199DC500D79B06; - remoteInfo = FLAnimatedImage; + remoteGlobalIDString = D1ED2D351AD2D09F00CFC3EB; + remoteInfo = "Kingfisher-iOS"; }; - 152FBCEF219799FC0079B3E8 /* PBXContainerItemProxy */ = { + 156370B1219FE22E00D51D42 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D13F49D61BEDA67C00CE335D; + remoteInfo = "Kingfisher-tvOS"; + }; + 156370B3219FE22E00D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4B2944481C3D01B20088C3E7; + remoteInfo = "Kingfisher-macOS"; + }; + 156370B5219FE22E00D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D16799EB1C4E74460020FD12; + remoteInfo = "Kingfisher-watchOS"; + }; + 156370B7219FE22E00D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D1ED2D3F1AD2D09F00CFC3EB; + remoteInfo = KingfisherTests; + }; + 156370B9219FE22E00D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4B37667F1C478F940001443F; + remoteInfo = "KingfisherTests-tvOS"; + }; + 156370BB219FE22E00D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D10EC2311C3D632300A4211C; + remoteInfo = "KingfisherTests-macOS"; + }; + 156370BF219FE24200D51D42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; proxyType = 1; - remoteGlobalIDString = 92C9BC0B1B199DC500D79B06; - remoteInfo = FLAnimatedImage; + remoteGlobalIDString = D1ED2D341AD2D09F00CFC3EB; + remoteInfo = "Kingfisher-iOS"; }; 157405C2215890BC00EEAAEB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -233,8 +276,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 156370BE219FE24200D51D42 /* Kingfisher.framework in Embed Frameworks */, 151AD4D9216899AD00F07403 /* AlamofireImage.framework in Embed Frameworks */, - 152FBCEE219799FC0079B3E8 /* FLAnimatedImage.framework in Embed Frameworks */, 151AD4E621689A0F00F07403 /* Alamofire.framework in Embed Frameworks */, EAD1445D219A6CAF002C5338 /* AlamofireNetworkActivityIndicator.framework in Embed Frameworks */, ); @@ -258,11 +301,12 @@ 152FB0F9218ADDD0001D6574 /* AttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentViewController.swift; sourceTree = ""; }; 152FBCD1219682E80079B3E8 /* AbstractStatusTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractStatusTableViewController.swift; sourceTree = ""; }; 152FBCE521978C4A0079B3E8 /* CellHeightManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellHeightManager.swift; sourceTree = ""; }; - 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FLAnimatedImage.xcodeproj; path = Frameworks/FLAnimatedImage/FLAnimatedImage.xcodeproj; sourceTree = ""; }; 152FBCF1219818AD0079B3E8 /* FieldTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldTableViewCell.swift; sourceTree = ""; }; + 15341865219FF29D002F5F8A /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = ""; }; + 1534187F219FF40F002F5F8A /* UIImageView+Effects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Effects.swift"; sourceTree = ""; }; 1539509021894A38009BA6E7 /* AlertManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertManager.swift; sourceTree = ""; }; + 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Kingfisher.xcodeproj; path = Frameworks/Kingfisher/Kingfisher.xcodeproj; sourceTree = ""; }; 1569029F219A7D75002BF61F /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = ""; }; - 156902B2219A8A1C002BF61F /* FLAnimatedImageView+SetImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FLAnimatedImageView+SetImage.swift"; sourceTree = ""; }; 156FF014217289380074D9CA /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = ""; }; 156FF0302174797E0074D9CA /* StatusTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewController.swift; sourceTree = ""; }; 156FF04E2175CDBC0074D9CA /* MainStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainStatusTableViewCell.swift; sourceTree = ""; }; @@ -309,8 +353,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 156370BD219FE24200D51D42 /* Kingfisher.framework in Frameworks */, 15A79B2E215C63B6007A326E /* AlamofireImage.framework in Frameworks */, - 152FBCED219799FC0079B3E8 /* FLAnimatedImage.framework in Frameworks */, 157405D1215890D700EEAAEB /* Alamofire.framework in Frameworks */, EAD1445C219A6CAF002C5338 /* AlamofireNetworkActivityIndicator.framework in Frameworks */, ); @@ -353,6 +397,7 @@ 15A79B42215EB959007A326E /* CoreDataManager.swift */, 157405B32151A93E00EEAAEB /* InstancesDataManager.swift */, 15F998342162C0E8009E58DA /* MastodonDataManager.swift */, + 15341865219FF29D002F5F8A /* SettingsManager.swift */, ); name = Managers; sourceTree = ""; @@ -388,9 +433,9 @@ isa = PBXGroup; children = ( 159026CF2163069600D362DD /* Date+TimeAgo.swift */, - 156902B2219A8A1C002BF61F /* FLAnimatedImageView+SetImage.swift */, 15B127912192467F00F4EF1D /* String+HtmlAttributed.swift */, 15B127A22192486200F4EF1D /* UIColor+HexString.swift */, + 1534187F219FF40F002F5F8A /* UIImageView+Effects.swift */, ); name = Extensions; sourceTree = ""; @@ -412,10 +457,16 @@ name = "Abstract View Controllers"; sourceTree = ""; }; - 152FBCE8219799E50079B3E8 /* Products */ = { + 15637095219FE22E00D51D42 /* Products */ = { isa = PBXGroup; children = ( - 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */, + 156370B0219FE22E00D51D42 /* Kingfisher.framework */, + 156370B2219FE22E00D51D42 /* Kingfisher.framework */, + 156370B4219FE22E00D51D42 /* Kingfisher.framework */, + 156370B6219FE22E00D51D42 /* Kingfisher.framework */, + 156370B8219FE22E00D51D42 /* KingfisherTests.xctest */, + 156370BA219FE22E00D51D42 /* KingfisherTests-tvOS.xctest */, + 156370BC219FE22E00D51D42 /* KingfisherTests-macOS.xctest */, ); name = Products; sourceTree = ""; @@ -457,7 +508,7 @@ 157405B7215890BC00EEAAEB /* Alamofire.xcodeproj */, 1517EA6F2159D72200DE80D6 /* AlamofireImage.xcodeproj */, 1522EC752193EBA10082C3FA /* AlamofireNetworkActivityIndicator.xcodeproj */, - 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */, + 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */, 15960E59213145E100C38CE9 /* elpha-ios */, 15960E58213145E100C38CE9 /* Products */, 157405D0215890D700EEAAEB /* Frameworks */, @@ -542,8 +593,8 @@ dependencies = ( 151AD4DB216899AD00F07403 /* PBXTargetDependency */, 151AD4E821689A0F00F07403 /* PBXTargetDependency */, - 152FBCF0219799FC0079B3E8 /* PBXTargetDependency */, EAD1445F219A6CAF002C5338 /* PBXTargetDependency */, + 156370C0219FE24200D51D42 /* PBXTargetDependency */, ); name = "elpha-ios"; productName = "elpha-ios"; @@ -591,8 +642,8 @@ ProjectRef = 1522EC752193EBA10082C3FA /* AlamofireNetworkActivityIndicator.xcodeproj */; }, { - ProductGroup = 152FBCE8219799E50079B3E8 /* Products */; - ProjectRef = 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */; + ProductGroup = 15637095219FE22E00D51D42 /* Products */; + ProjectRef = 15637094219FE22E00D51D42 /* Kingfisher.xcodeproj */; }, ); projectRoot = ""; @@ -673,11 +724,53 @@ remoteRef = 1522EC7D2193EBA10082C3FA /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */ = { + 156370B0219FE22E00D51D42 /* Kingfisher.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Kingfisher.framework; + remoteRef = 156370AF219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370B2219FE22E00D51D42 /* Kingfisher.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Kingfisher.framework; + remoteRef = 156370B1219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370B4219FE22E00D51D42 /* Kingfisher.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; - path = FLAnimatedImage.framework; - remoteRef = 152FBCEB219799E50079B3E8 /* PBXContainerItemProxy */; + path = Kingfisher.framework; + remoteRef = 156370B3219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370B6219FE22E00D51D42 /* Kingfisher.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Kingfisher.framework; + remoteRef = 156370B5219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370B8219FE22E00D51D42 /* KingfisherTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = KingfisherTests.xctest; + remoteRef = 156370B7219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370BA219FE22E00D51D42 /* KingfisherTests-tvOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "KingfisherTests-tvOS.xctest"; + remoteRef = 156370B9219FE22E00D51D42 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 156370BC219FE22E00D51D42 /* KingfisherTests-macOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "KingfisherTests-macOS.xctest"; + remoteRef = 156370BB219FE22E00D51D42 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 157405C3215890BC00EEAAEB /* Alamofire.framework */ = { @@ -776,9 +869,11 @@ 15131EF2216D8D570092B252 /* StatusView.swift in Sources */, 152734D22186DC74003DB3C8 /* TimelinesViewController.swift in Sources */, 15B127A32192486200F4EF1D /* UIColor+HexString.swift in Sources */, + 15341880219FF40F002F5F8A /* UIImageView+Effects.swift in Sources */, 156FF07221779C650074D9CA /* InstanceRequest.swift in Sources */, 15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */, 156FF07021779C570074D9CA /* MastodonAPI.swift in Sources */, + 15341866219FF29D002F5F8A /* SettingsManager.swift in Sources */, 15C91A04216AB32500D97DC3 /* AlertView.swift in Sources */, 15131EF6216DBA820092B252 /* AccountNavigationController.swift in Sources */, 15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */, @@ -788,7 +883,6 @@ 159026D02163069600D362DD /* Date+TimeAgo.swift in Sources */, 152FBCD2219682E80079B3E8 /* AbstractStatusTableViewController.swift in Sources */, 15A79B43215EB959007A326E /* CoreDataManager.swift in Sources */, - 156902B3219A8A1C002BF61F /* FLAnimatedImageView+SetImage.swift in Sources */, 15BB72AB2171A8D4002F1FA4 /* TimelinesTableViewCell.swift in Sources */, 1574148D2169AD0100C841BD /* AttachmentManager.swift in Sources */, 156FF015217289380074D9CA /* AccountTableViewCell.swift in Sources */, @@ -814,10 +908,10 @@ name = "Alamofire iOS"; targetProxy = 151AD4E721689A0F00F07403 /* PBXContainerItemProxy */; }; - 152FBCF0219799FC0079B3E8 /* PBXTargetDependency */ = { + 156370C0219FE24200D51D42 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = FLAnimatedImage; - targetProxy = 152FBCEF219799FC0079B3E8 /* PBXContainerItemProxy */; + name = "Kingfisher-iOS"; + targetProxy = 156370BF219FE24200D51D42 /* PBXContainerItemProxy */; }; EAD1445F219A6CAF002C5338 /* PBXTargetDependency */ = { isa = PBXTargetDependency; diff --git a/elpha-ios/AccountTableViewController.swift b/elpha-ios/AccountTableViewController.swift index 92d505b..9367349 100644 --- a/elpha-ios/AccountTableViewController.swift +++ b/elpha-ios/AccountTableViewController.swift @@ -8,7 +8,7 @@ import AlamofireImage import CoreData -import FLAnimatedImage +import Kingfisher import UIKit import SafariServices @@ -66,7 +66,7 @@ class FieldTableViewController: NSObject, UITableViewDelegate, UITableViewDataSo class AccountTableViewController: AbstractStatusTableViewController { @IBOutlet var headerView: UIView! @IBOutlet var headerImageView: UIImageView! - @IBOutlet var avatarImageView: FLAnimatedImageView! + @IBOutlet var avatarImageView: UIImageView! @IBOutlet var displayNameLabel: UILabel! @IBOutlet var usernameLabel: UILabel! @IBOutlet var statusesLabel: UILabel! @@ -99,48 +99,14 @@ class AccountTableViewController: AbstractStatusTableViewController { fetch() } - private func updateHeader(withAccount account: AccountMO) { - navigationItem.title = account.displayName - - if let headerURL = account.headerURL { - headerImageView.af_setImage(withURL: headerURL) - } - - if let avatarURL = account.avatarURL { - avatarImageView.setImage(withURL: avatarURL, withShadow: true) - } - - displayNameLabel.text = account.displayName - usernameLabel.text = "@\(account.acct!)" - - if let note = account.note { - contentTextView.attributedText = note.htmlAttributed(size: 15, centered: true) - } - - statusesLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.statusesCount), number: .decimal) - followingLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followingCount), number: .decimal) - followersLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followersCount), number: .decimal) - - if let fields = account.fields { - fieldTableViewController!.fields = fields - - fieldTableView.dataSource = fieldTableViewController - fieldTableView.delegate = fieldTableViewController - - fieldTableView.isHidden = false - } else { - fieldTableView.isHidden = true - } - } - override func viewDidLoad() { super.viewDidLoad() refreshControl?.addTarget(self, action: #selector(self.fetch), for: .valueChanged) fieldTableViewController = FieldTableViewController() - avatarImageView.layer.cornerRadius = 10 - avatarImageView.layer.masksToBounds = true + avatarImageView.setRoundedCorners() + avatarImageView.setShadow() if self.account == nil { if let session = AuthenticationManager.session { @@ -175,6 +141,41 @@ class AccountTableViewController: AbstractStatusTableViewController { } } + private func updateHeader(withAccount account: AccountMO) { + navigationItem.title = account.displayName + + if let headerURL = account.headerURL { + headerImageView.kf.setImage(with: headerURL) + } + + if let avatarURL = account.avatarURL { + let options: KingfisherOptionsInfo = SettingsManager.automaticallyPlayGIFs ? [] : [.onlyLoadFirstFrame] + avatarImageView.kf.setImage(with: avatarURL, options: options) + } + + displayNameLabel.text = account.displayName + usernameLabel.text = "@\(account.acct!)" + + if let note = account.note { + contentTextView.attributedText = note.htmlAttributed(size: 15, centered: true) + } + + statusesLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.statusesCount), number: .decimal) + followingLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followingCount), number: .decimal) + followersLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followersCount), number: .decimal) + + if let fields = account.fields { + fieldTableViewController!.fields = fields + + fieldTableView.dataSource = fieldTableViewController + fieldTableView.delegate = fieldTableViewController + + fieldTableView.isHidden = false + } else { + fieldTableView.isHidden = true + } + } + override func loadMoreTapped(status: StatusMO, direction: PaginationDirection) { if let markers = status.markers { markers.forEach { marker in diff --git a/elpha-ios/AttachmentManager.swift b/elpha-ios/AttachmentManager.swift index 90b25f8..5be32aa 100644 --- a/elpha-ios/AttachmentManager.swift +++ b/elpha-ios/AttachmentManager.swift @@ -7,7 +7,7 @@ // import AlamofireImage -import FLAnimatedImage +import Kingfisher import UIKit protocol AttachmentManagerDelegate { @@ -28,8 +28,13 @@ class AttachmentManager: NSObject { return } + let placeholder = UIImage(named: "Help") let halfWidth = (view.frame.width / 2) - 1 + let fullAttachmentSize = CGSize(width: view.frame.width, height: view.frame.width) + let tallAttachmentSize = CGSize(width: halfWidth, height: view.frame.width) + let smallAttachmentSize = CGSize(width: halfWidth, height: halfWidth) + for subview in view.subviews as [UIView] { subview.removeFromSuperview() } @@ -39,8 +44,12 @@ class AttachmentManager: NSObject { return case 1: let attachment = attachments.firstObject as! AttachmentMO - let imageView = FLAnimatedImageView() + let processor = ResizingImageProcessor(referenceSize: fullAttachmentSize, mode: .aspectFill) + + let imageView = UIImageView() imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + imageView.isUserInteractionEnabled = true + imageView.kf.setImage(with: attachment.url!, placeholder: placeholder, options: [.processor(processor)]) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(attachmentTapped)) tapGestureRecognizer.delegate = self @@ -49,9 +58,6 @@ class AttachmentManager: NSObject { tapGestureRecognizer.name = "0" imageView.addGestureRecognizer(tapGestureRecognizer) - imageView.isUserInteractionEnabled = true - - imageView.setImage(withURL: attachment.url!) view.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false @@ -64,12 +70,17 @@ class AttachmentManager: NSObject { ]) case 2: let imageViews = [ - FLAnimatedImageView(), - FLAnimatedImageView(), + UIImageView(), + UIImageView(), ] for (index, imageView) in imageViews.enumerated() { + let attachment = attachments[index] as! AttachmentMO + let processor = ResizingImageProcessor(referenceSize: tallAttachmentSize, mode: .aspectFill) + imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + imageView.isUserInteractionEnabled = true + imageView.kf.setImage(with: attachment.url!, placeholder: placeholder, options: [.processor(processor)]) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(attachmentTapped)) tapGestureRecognizer.delegate = self @@ -78,9 +89,6 @@ class AttachmentManager: NSObject { tapGestureRecognizer.name = String(index) imageView.addGestureRecognizer(tapGestureRecognizer) - imageView.isUserInteractionEnabled = true - - imageView.setImage(withURL: (attachments[index] as! AttachmentMO).url!) view.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false @@ -98,13 +106,24 @@ class AttachmentManager: NSObject { ]) case 3: let imageViews = [ - FLAnimatedImageView(), - FLAnimatedImageView(), - FLAnimatedImageView(), + UIImageView(), + UIImageView(), + UIImageView(), ] for (index, imageView) in imageViews.enumerated() { + let attachment = attachments[index] as! AttachmentMO + var processor: ImageProcessor + + if index == 0 { + processor = ResizingImageProcessor(referenceSize: tallAttachmentSize, mode: .aspectFill) + } else { + processor = ResizingImageProcessor(referenceSize: smallAttachmentSize, mode: .aspectFill) + } + imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + imageView.isUserInteractionEnabled = true + imageView.kf.setImage(with: attachment.url!, placeholder: placeholder, options: [.processor(processor)]) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(attachmentTapped)) tapGestureRecognizer.delegate = self @@ -113,9 +132,6 @@ class AttachmentManager: NSObject { tapGestureRecognizer.name = String(index) imageView.addGestureRecognizer(tapGestureRecognizer) - imageView.isUserInteractionEnabled = true - - imageView.setImage(withURL: (attachments[index] as! AttachmentMO).url!) view.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false @@ -142,14 +158,19 @@ class AttachmentManager: NSObject { ]) default: let imageViews = [ - FLAnimatedImageView(), - FLAnimatedImageView(), - FLAnimatedImageView(), - FLAnimatedImageView(), + UIImageView(), + UIImageView(), + UIImageView(), + UIImageView(), ] for (index, imageView) in imageViews.enumerated() { + let attachment = attachments[index] as! AttachmentMO + let processor = ResizingImageProcessor(referenceSize: smallAttachmentSize, mode: .aspectFill) + imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + imageView.isUserInteractionEnabled = true + imageView.kf.setImage(with: attachment.url!, placeholder: placeholder, options: [.processor(processor)]) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(attachmentTapped)) tapGestureRecognizer.delegate = self @@ -158,9 +179,6 @@ class AttachmentManager: NSObject { tapGestureRecognizer.name = String(index) imageView.addGestureRecognizer(tapGestureRecognizer) - imageView.isUserInteractionEnabled = true - - imageView.setImage(withURL: (attachments[index] as! AttachmentMO).url!) view.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false diff --git a/elpha-ios/AttachmentViewController.swift b/elpha-ios/AttachmentViewController.swift index 61ea4b8..cc70bbe 100644 --- a/elpha-ios/AttachmentViewController.swift +++ b/elpha-ios/AttachmentViewController.swift @@ -6,12 +6,12 @@ // Copyright © 2018 Elpha. All rights reserved. // -import FLAnimatedImage +import Kingfisher import UIKit class AttachmentViewController: UIViewController { @IBOutlet var attachmentScrollView: UIScrollView! - @IBOutlet var attachmentImageView: FLAnimatedImageView! + @IBOutlet var attachmentImageView: UIImageView! @IBOutlet var statusTextView: UITextView! var attachment: AttachmentMO? = nil @@ -34,7 +34,7 @@ class AttachmentViewController: UIViewController { if let attachment = attachment { attachmentScrollView.delegate = self - attachmentImageView.setImage(withURL: attachment.url!, withShadow: false, withContentMode: .scaleAspectFit) + attachmentImageView.kf.setImage(with: attachment.url!) if let content = attachment.status?.content { statusTextView.attributedText = content.htmlAttributed(size: 14, centered: true, color: UIColor.white) diff --git a/elpha-ios/Base.lproj/Main.storyboard b/elpha-ios/Base.lproj/Main.storyboard index 0fa66f5..c0ced32 100644 --- a/elpha-ios/Base.lproj/Main.storyboard +++ b/elpha-ios/Base.lproj/Main.storyboard @@ -493,7 +493,7 @@ - + @@ -807,7 +807,7 @@ - + @@ -879,6 +879,9 @@ + + + @@ -943,6 +946,7 @@ + @@ -1083,7 +1087,7 @@ - + @@ -1322,7 +1326,7 @@ - + @@ -1405,7 +1409,7 @@ - + diff --git a/elpha-ios/ComposeViewController.swift b/elpha-ios/ComposeViewController.swift index 6639840..7f374ad 100644 --- a/elpha-ios/ComposeViewController.swift +++ b/elpha-ios/ComposeViewController.swift @@ -6,12 +6,12 @@ // Copyright © 2018 Elpha. All rights reserved. // -import FLAnimatedImage +import Kingfisher import UIKit class ComposeViewController: UIViewController { @IBOutlet var replyToView: UIView! - @IBOutlet var replyToAvatarImageView: FLAnimatedImageView! + @IBOutlet var replyToAvatarImageView: UIImageView! @IBOutlet var replyToDisplayNameLabel: UILabel! @IBOutlet var replyToUsernameLabel: UILabel! @IBOutlet var replyToContentTextView: UITextViewFixed! @@ -31,12 +31,11 @@ class ComposeViewController: UIViewController { feedbackGenerator = UINotificationFeedbackGenerator() - replyToAvatarImageView.layer.cornerRadius = 10 - replyToAvatarImageView.layer.masksToBounds = true - statusTextView.layer.cornerRadius = 10 statusTextView.layer.masksToBounds = true + replyToAvatarImageView.setRoundedCorners() + composeAccessoryView = ComposeAccessoryView(frame: CGRect(x: 0.0, y: 0.0, width: self.view.bounds.size.width, height: composeAccessoryViewHeight)) composeAccessoryView!.delegate = self composeAccessoryView!.selectedVisibility = .public @@ -70,7 +69,8 @@ class ComposeViewController: UIViewController { func setupReplyTo(status: StatusMO) { if let account = status.account { - replyToAvatarImageView.setImage(withURL: account.avatarURL!) + let options: KingfisherOptionsInfo = SettingsManager.automaticallyPlayGIFs ? [] : [.onlyLoadFirstFrame] + replyToAvatarImageView.kf.setImage(with: account.avatarURL!, options: options) replyToDisplayNameLabel.text = account.displayName replyToUsernameLabel.text = "@\(account.acct!)" } @@ -86,7 +86,7 @@ class ComposeViewController: UIViewController { if endFrameY >= UIScreen.main.bounds.size.height { bottomConstraint.constant = 0 } else { - bottomConstraint.constant = (endFrame?.size.height ?? 0.0) - 15 + bottomConstraint.constant = (endFrame?.size.height ?? 0.0) + 10 } } } diff --git a/elpha-ios/FLAnimatedImageView+SetImage.swift b/elpha-ios/FLAnimatedImageView+SetImage.swift deleted file mode 100644 index 8952b66..0000000 --- a/elpha-ios/FLAnimatedImageView+SetImage.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// FLAnimatedImage+SetImage.swift -// elpha-ios -// -// Created by Dwayne Harris on 11/12/18. -// Copyright © 2018 Elpha. All rights reserved. -// - -import Alamofire -import AlamofireImage -import FLAnimatedImage -import Foundation - -class ImageCache { - static var cache: NSCache = NSCache() - - static func set(string: String, data: Data) { - ImageCache.cache.setObject(data as AnyObject, forKey: string as AnyObject) - } - - static func set(url: URL, data: Data) { - set(string: url.absoluteString, data: data) - } - - static func set(request: URLRequest, data: Data) { - set(url: request.url!, data: data) - } - - static func get(string: String) -> Data? { - if let data = ImageCache.cache.object(forKey: string as AnyObject) as? Data { - return data - } else { - return nil - } - } - - static func get(url: URL) -> Data? { - return get(string: url.absoluteString) - } - - static func get(request: URLRequest) -> Data? { - return get(url: request.url!) - } -} - -extension FLAnimatedImageView { - func setPlaceholder() { - self.contentMode = .center - self.image = UIImage(named: "Help") - } - - func setImage(withURL url: URL, withShadow shadow: Bool = false, withContentMode contentMode: UIView.ContentMode = .scaleAspectFill) { - func setShadow() { - if shadow { - self.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).cgColor - self.layer.shadowOpacity = 1 - self.layer.shadowOffset = CGSize.zero - self.layer.shadowRadius = 10 - } - } - - if let data = ImageCache.get(url: url) { - self.contentMode = contentMode - - if url.absoluteString.hasSuffix(".gif") { - self.animatedImage = FLAnimatedImage(animatedGIFData: data) - } else { - self.image = UIImage(data: data) - } - - setShadow() - } else { - setPlaceholder() - - Alamofire.request(url).responseImage { response in - if let data = response.data { - DispatchQueue.main.async { - ImageCache.set(url: url, data: data) - - self.contentMode = contentMode - - if url.absoluteString.hasSuffix(".gif") { - self.animatedImage = FLAnimatedImage(animatedGIFData: data) - } else { - self.image = UIImage(data: data) - } - - setShadow() - } - } - } - } - } -} diff --git a/elpha-ios/MainStatusTableViewCell.swift b/elpha-ios/MainStatusTableViewCell.swift index c5a27c2..edd05e5 100644 --- a/elpha-ios/MainStatusTableViewCell.swift +++ b/elpha-ios/MainStatusTableViewCell.swift @@ -6,11 +6,10 @@ // Copyright © 2018 Elpha. All rights reserved. // -import FLAnimatedImage import UIKit class MainStatusTableViewCell: UITableViewCell { - @IBOutlet var avatarImageView: FLAnimatedImageView! + @IBOutlet var avatarImageView: UIImageView! @IBOutlet var displayNameLabel: UILabel! @IBOutlet var usernameLabel: UILabel! @IBOutlet var repliesImageView: UIImageView! diff --git a/elpha-ios/MastodonAPI.swift b/elpha-ios/MastodonAPI.swift index 6b9f3be..6c5f42b 100644 --- a/elpha-ios/MastodonAPI.swift +++ b/elpha-ios/MastodonAPI.swift @@ -25,6 +25,10 @@ enum PaginationDirection: String { case prev, next } +enum AttachmentType: String { + case unknown, image, gifv, video +} + enum StatusVisibility: String { case direct, `private`, unlisted, `public` } diff --git a/elpha-ios/SettingsManager.swift b/elpha-ios/SettingsManager.swift new file mode 100644 index 0000000..48d898a --- /dev/null +++ b/elpha-ios/SettingsManager.swift @@ -0,0 +1,21 @@ +// +// SettingsManager.swift +// elpha-ios +// +// Created by Dwayne Harris on 11/16/18. +// Copyright © 2018 Elpha. All rights reserved. +// + +import Foundation + +class SettingsManager { + public static var automaticallyPlayGIFs: Bool { + get { + return UserDefaults.standard.bool(forKey: "settings:gif") + } + + set { + UserDefaults.standard.set(newValue, forKey: "settings:gif") + } + } +} diff --git a/elpha-ios/SettingsTableViewController.swift b/elpha-ios/SettingsTableViewController.swift index 201aaef..a3569f8 100644 --- a/elpha-ios/SettingsTableViewController.swift +++ b/elpha-ios/SettingsTableViewController.swift @@ -7,29 +7,34 @@ // import AlamofireImage -import FLAnimatedImage +import Kingfisher import UIKit class SettingsTableViewController: UITableViewController { @IBOutlet var headerImageView: UIImageView! - @IBOutlet var avatarImageView: FLAnimatedImageView! + @IBOutlet var avatarImageView: UIImageView! @IBOutlet var displayNameLabel: UILabel! @IBOutlet var usernameLabel: UILabel! + @IBOutlet var gifSwitch: UISwitch! override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Settings" - avatarImageView.layer.cornerRadius = 10 - avatarImageView.layer.masksToBounds = true + avatarImageView.setShadow() + avatarImageView.setRoundedCorners() if let account = AuthenticationManager.session?.account { - headerImageView.af_setImage(withURL: account.headerURL!) - avatarImageView.setImage(withURL: account.avatarURL!, withShadow: true) + let options: KingfisherOptionsInfo = SettingsManager.automaticallyPlayGIFs ? [] : [.onlyLoadFirstFrame] + + headerImageView.kf.setImage(with: account.headerURL!) + avatarImageView.kf.setImage(with: account.avatarURL!, options: options) displayNameLabel.text = account.displayName usernameLabel.text = "@\(account.acct!)" } + + gifSwitch.isOn = SettingsManager.automaticallyPlayGIFs } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -44,4 +49,8 @@ class SettingsTableViewController: UITableViewController { return } } + + @IBAction func gifChanged(_ sender: Any) { + SettingsManager.automaticallyPlayGIFs = gifSwitch.isOn + } } diff --git a/elpha-ios/StatusTableViewController.swift b/elpha-ios/StatusTableViewController.swift index f35c0ef..6bdf8f3 100644 --- a/elpha-ios/StatusTableViewController.swift +++ b/elpha-ios/StatusTableViewController.swift @@ -8,6 +8,7 @@ import AlamofireImage import CoreData +import Kingfisher import UIKit import SafariServices @@ -267,13 +268,12 @@ extension StatusTableViewController { } cell.contentTextView.delegate = self - - cell.avatarImageView.layer.cornerRadius = 10 - cell.avatarImageView.layer.masksToBounds = true + cell.avatarImageView.setRoundedCorners() func updateAccountView(status: StatusMO) { if let account = status.account { - cell.avatarImageView.setImage(withURL: account.avatarURL!, withShadow: true) + let options: KingfisherOptionsInfo = SettingsManager.automaticallyPlayGIFs ? [] : [.onlyLoadFirstFrame] + cell.avatarImageView.kf.setImage(with: account.avatarURL!, options: options) cell.displayNameLabel.text = account.displayName cell.usernameLabel.text = "@\(account.acct!)" } diff --git a/elpha-ios/StatusView.swift b/elpha-ios/StatusView.swift index 43676d6..7a61195 100644 --- a/elpha-ios/StatusView.swift +++ b/elpha-ios/StatusView.swift @@ -7,7 +7,7 @@ // import AlamofireImage -import FLAnimatedImage +import Kingfisher import UIKit protocol StatusViewDelegate { @@ -35,14 +35,14 @@ extension StatusViewDelegate { class StatusView: UIView { @IBOutlet var contentView: UIView! @IBOutlet var boostView: UIView! - @IBOutlet var boostAvatarImageView: FLAnimatedImageView! + @IBOutlet var boostAvatarImageView: UIImageView! @IBOutlet var boostDisplayNameLabel: UILabel! @IBOutlet var boostUsernameLabel: UILabel! @IBOutlet var replyView: UIView! - @IBOutlet var replyAvatarImageView: FLAnimatedImageView! + @IBOutlet var replyAvatarImageView: UIImageView! @IBOutlet var replyDisplayNameLabel: UILabel! @IBOutlet var replyUsernameLabel: UILabel! - @IBOutlet var avatarImageView: FLAnimatedImageView! + @IBOutlet var avatarImageView: UIImageView! @IBOutlet var displayNameLabel: UILabel! @IBOutlet var usernameLabel: UILabel! @IBOutlet var timestampLabel: UILabel! @@ -238,12 +238,9 @@ class StatusView: UIView { contentTextView.delegate = self feedbackGenerator = UINotificationFeedbackGenerator() - avatarImageView.layer.cornerRadius = 10 - avatarImageView.layer.masksToBounds = true - boostAvatarImageView.layer.cornerRadius = 10 - boostAvatarImageView.layer.masksToBounds = true - replyAvatarImageView.layer.cornerRadius = 10 - replyAvatarImageView.layer.masksToBounds = true + avatarImageView.setRoundedCorners() + boostAvatarImageView.setRoundedCorners() + replyAvatarImageView.setRoundedCorners() } public func update(withStatus status: StatusMO) { @@ -255,9 +252,11 @@ class StatusView: UIView { attachmentsView.isHidden = true spoilerImageView.isHidden = true + let avatarOptions: KingfisherOptionsInfo = SettingsManager.automaticallyPlayGIFs ? [] : [.onlyLoadFirstFrame] + func updateStatusContent(_ status: StatusMO) { if let account = status.account { - avatarImageView.setImage(withURL: account.avatarURL!) + avatarImageView.kf.setImage(with: account.avatarURL!, options: avatarOptions) displayNameLabel.text = account.displayName usernameLabel.text = "@\(account.acct!)" } @@ -349,7 +348,7 @@ class StatusView: UIView { boostView.isHidden = false if let account = status.account { - boostAvatarImageView.setImage(withURL: account.avatarURL!) + boostAvatarImageView.kf.setImage(with: account.avatarURL!, options: avatarOptions) boostDisplayNameLabel.text = account.displayName boostUsernameLabel.text = "@\(account.acct!)" } @@ -357,7 +356,7 @@ class StatusView: UIView { updateStatusContent(reblog) } else { if let account = status.account { - avatarImageView.setImage(withURL: account.avatarURL!) + avatarImageView.kf.setImage(with: account.avatarURL!, options: avatarOptions) displayNameLabel.text = account.displayName usernameLabel.text = "@\(account.acct!)" } @@ -368,7 +367,7 @@ class StatusView: UIView { if let replyAccountID = status.inReplyToAccountID { if let replyAccount = MastodonDataManager.account(id: replyAccountID) { replyView.isHidden = false - replyAvatarImageView.setImage(withURL: replyAccount.avatarURL!) + replyAvatarImageView.kf.setImage(with: replyAccount.avatarURL!, options: avatarOptions) replyDisplayNameLabel.text = replyAccount.displayName replyUsernameLabel.text = "@\(replyAccount.acct!)" } diff --git a/elpha-ios/StatusView.xib b/elpha-ios/StatusView.xib index 1e95e35..5b6f198 100644 --- a/elpha-ios/StatusView.xib +++ b/elpha-ios/StatusView.xib @@ -91,7 +91,7 @@ - + @@ -159,7 +159,7 @@ - + @@ -230,7 +230,7 @@ - + @@ -394,7 +394,7 @@ - + @@ -585,7 +585,7 @@ - + diff --git a/elpha-ios/UIImageView+Effects.swift b/elpha-ios/UIImageView+Effects.swift new file mode 100644 index 0000000..c6aea72 --- /dev/null +++ b/elpha-ios/UIImageView+Effects.swift @@ -0,0 +1,23 @@ +// +// UIImageView+Effects.swift +// elpha-ios +// +// Created by Dwayne Harris on 11/16/18. +// Copyright © 2018 Elpha. All rights reserved. +// + +import UIKit + +extension UIImageView { + func setShadow() { + self.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).cgColor + self.layer.shadowOpacity = 1 + self.layer.shadowOffset = CGSize.zero + self.layer.shadowRadius = 10 + } + + func setRoundedCorners() { + self.layer.cornerRadius = 10 + self.layer.masksToBounds = true + } +}