diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index d8c55647e..9619b3fdd 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -88,6 +88,7 @@ 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; }; 516A09402361240900EAE89B /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A093F2361240900EAE89B /* Account.storyboard */; }; 516A09422361248000EAE89B /* Inspector.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A09412361248000EAE89B /* Inspector.storyboard */; }; + 516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */; }; 51707439232AA97100A461A3 /* ShareFolderPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */; }; 5170743A232AABFC00A461A3 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; }; 517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; }; @@ -1251,6 +1252,7 @@ 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; 516A093F2361240900EAE89B /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = ""; }; 516A09412361248000EAE89B /* Inspector.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Inspector.storyboard; sourceTree = ""; }; + 516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedTableViewSectionHeaderLayout.swift; sourceTree = ""; }; 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = ""; }; 517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = ""; }; 517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerWebViewProvider.swift; sourceTree = ""; }; @@ -1869,6 +1871,7 @@ isa = PBXGroup; children = ( 512E08F722688F7C00BDCFDD /* MasterFeedTableViewSectionHeader.swift */, + 516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */, 51C45262226508F600C03939 /* MasterFeedTableViewCell.swift */, 51C45263226508F600C03939 /* MasterFeedTableViewCellLayout.swift */, 51C45261226508F600C03939 /* MasterFeedUnreadCountView.swift */, @@ -3970,6 +3973,7 @@ 518651DA235621840078E021 /* ImageTransition.swift in Sources */, 514219372352510100E07E2C /* ImageScrollView.swift in Sources */, 51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */, + 516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */, 51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */, 84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */, 512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */, diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift index d82522d85..2088cfcf7 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift @@ -98,26 +98,14 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView { } override func sizeThatFits(_ size: CGSize) -> CGSize { - - let unreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero) - - // Since we can't reload Section Headers to reset the height after we get the - // unread count did change, we always assume a large unread count - // - // This means that sometimes on the second to largest font size will have extra - // space under the account name. This is better than having it overflow into the - // cell below. - unreadCountView.unreadCount = 888 - - let layout = MasterFeedTableViewCellLayout(cellWidth: size.width, insets: safeAreaInsets, label: titleView, unreadCountView: unreadCountView, showingEditingControl: false, indent: false, shouldShowDisclosure: true) - + let layout = MasterFeedTableViewSectionHeaderLayout(cellWidth: size.width, insets: safeAreaInsets, label: titleView, unreadCountView: unreadCountView) return CGSize(width: bounds.width, height: layout.height) } override func layoutSubviews() { super.layoutSubviews() - let layout = MasterFeedTableViewCellLayout(cellWidth: bounds.size.width, insets: safeAreaInsets, label: titleView, unreadCountView: unreadCountView, showingEditingControl: false, indent: false, shouldShowDisclosure: true) + let layout = MasterFeedTableViewSectionHeaderLayout(cellWidth: bounds.size.width, insets: safeAreaInsets, label: titleView, unreadCountView: unreadCountView) layoutWith(layout) } @@ -171,7 +159,7 @@ private extension MasterFeedTableViewSectionHeader { view.translatesAutoresizingMaskIntoConstraints = false } - func layoutWith(_ layout: MasterFeedTableViewCellLayout) { + func layoutWith(_ layout: MasterFeedTableViewSectionHeaderLayout) { titleView.setFrameIfNotEqual(layout.titleRect) unreadCountView.setFrameIfNotEqual(layout.unreadCountRect) disclosureView.setFrameIfNotEqual(layout.disclosureButtonRect) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift new file mode 100644 index 000000000..5b487f837 --- /dev/null +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift @@ -0,0 +1,85 @@ +// +// MasterFeedTableViewSectionHeaderLayout.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 11/5/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import UIKit +import RSCore + +struct MasterFeedTableViewSectionHeaderLayout { + + private static let labelMarginRight = CGFloat(integerLiteral: 8) + private static let unreadCountMarginRight = CGFloat(integerLiteral: 16) + private static let disclosureButtonSize = CGSize(width: 44, height: 44) + private static let verticalPadding = CGFloat(integerLiteral: 11) + + private static let minRowHeight = CGFloat(integerLiteral: 44) + + let titleRect: CGRect + let unreadCountRect: CGRect + let disclosureButtonRect: CGRect + + let height: CGFloat + + init(cellWidth: CGFloat, insets: UIEdgeInsets, label: UILabel, unreadCountView: MasterFeedUnreadCountView) { + + let bounds = CGRect(x: insets.left, y: 0.0, width: floor(cellWidth - insets.right), height: 0.0) + + // Disclosure Button + var rDisclosure = CGRect.zero + rDisclosure.size = MasterFeedTableViewSectionHeaderLayout.disclosureButtonSize + rDisclosure.origin.x = bounds.origin.x + + // Unread Count + let unreadCountSize = unreadCountView.contentSize + let unreadCountIsHidden = unreadCountView.unreadCount < 1 + + var rUnread = CGRect.zero + if !unreadCountIsHidden { + rUnread.size = unreadCountSize + rUnread.origin.x = bounds.maxX - (MasterFeedTableViewSectionHeaderLayout.unreadCountMarginRight + unreadCountSize.width) + } + + // Max Unread Count + // We can't reload Section Headers so we don't let the title extend into the (probably) worse case Unread Count area. + let maxUnreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero) + maxUnreadCountView.unreadCount = 888 + let maxUnreadCountSize = maxUnreadCountView.contentSize + + // Title + let rLabelx = insets.left + MasterFeedTableViewSectionHeaderLayout.disclosureButtonSize.width + let rLabely = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewSectionHeaderLayout.verticalPadding) + + var labelWidth = CGFloat.zero + labelWidth = cellWidth - (rLabelx + MasterFeedTableViewSectionHeaderLayout.labelMarginRight + maxUnreadCountSize.width + MasterFeedTableViewSectionHeaderLayout.unreadCountMarginRight) + + let labelSizeInfo = MultilineUILabelSizer.size(for: label.text ?? "", font: label.font, numberOfLines: 0, width: Int(floor(labelWidth))) + let rLabel = CGRect(x: rLabelx, y: rLabely, width: labelWidth, height: labelSizeInfo.size.height) + + // Determine cell height + let paddedLabelHeight = rLabel.maxY + UIFontMetrics.default.scaledValue(for: MasterFeedTableViewSectionHeaderLayout.verticalPadding) + let maxGraphicsHeight = [rUnread, rDisclosure].maxY() + var cellHeight = max(paddedLabelHeight, maxGraphicsHeight) + if cellHeight < MasterFeedTableViewSectionHeaderLayout.minRowHeight { + cellHeight = MasterFeedTableViewSectionHeaderLayout.minRowHeight + } + + // Center in Cell + let newBounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: cellHeight) + if !unreadCountIsHidden { + rUnread = MasterFeedTableViewCellLayout.centerVertically(rUnread, newBounds) + } + rDisclosure = MasterFeedTableViewCellLayout.centerVertically(rDisclosure, newBounds) + + // Assign the properties + self.height = cellHeight + self.unreadCountRect = rUnread + self.disclosureButtonRect = rDisclosure + self.titleRect = rLabel + + } + +}