diff --git a/Metatext.xcodeproj/project.pbxproj b/Metatext.xcodeproj/project.pbxproj index 5c97b7d..48e1b2c 100644 --- a/Metatext.xcodeproj/project.pbxproj +++ b/Metatext.xcodeproj/project.pbxproj @@ -110,6 +110,7 @@ D0F2D4DB257F018300986197 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C6FAB252024BD003D0300 /* Array+Extensions.swift */; }; D0F2D54025818C4B00986197 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = D0F2D53F25818C4B00986197 /* Kingfisher */; }; D0F2D5452581ABAB00986197 /* KingfisherOptionsInfo+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */; }; + D0F2D54B2581CF7D00986197 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */; }; D0FE1C8F253686F9003EF1EB /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FE1C8E253686F9003EF1EB /* PlayerView.swift */; }; D0FE1C9825368A9D003EF1EB /* PlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FE1C9725368A9D003EF1EB /* PlayerCache.swift */; }; /* End PBXBuildFile section */ @@ -259,6 +260,7 @@ D0F0B12D251A97E400942152 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionItem+Extensions.swift"; sourceTree = ""; }; D0F2D4D0257EE84400986197 /* NewStatusDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusDataSource.swift; sourceTree = ""; }; + D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = ""; }; D0FE1C8E253686F9003EF1EB /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = ""; }; D0FE1C9725368A9D003EF1EB /* PlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerCache.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -464,6 +466,7 @@ D0C7D42E24F76169001EBDBB /* TabNavigationView.swift */, D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */, D0EA59472522B8B600804347 /* ViewConstants.swift */, + D0F2D54A2581CF7D00986197 /* VisualEffectBlur.swift */, D0E1F582251F13EC00D45315 /* WebfingerIndicatorView.swift */, ); path = Views; @@ -809,6 +812,7 @@ D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */, D0C7D4C424F7616A001EBDBB /* AppDelegate.swift in Sources */, D0C7D49924F7616A001EBDBB /* AddIdentityView.swift in Sources */, + D0F2D54B2581CF7D00986197 /* VisualEffectBlur.swift in Sources */, D0A7AC7325748BFF00E4E8AB /* ReportStatusView.swift in Sources */, D0C7D4C324F7616A001EBDBB /* MetatextApp.swift in Sources */, D0E1F583251F13EC00D45315 /* WebfingerIndicatorView.swift in Sources */, diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index 678482b..ad827cd 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -40,6 +40,7 @@ class TableViewController: UITableViewController { tableView.prefetchDataSource = self tableView.cellLayoutMarginsFollowReadableWidth = true tableView.tableFooterView = UIView() + tableView.contentInset.bottom = Self.bottomInset view.addSubview(webfingerIndicatorView) webfingerIndicatorView.translatesAutoresizingMaskIntoConstraints = false @@ -221,6 +222,8 @@ extension TableViewController: ZoomAnimatorDelegate { } private extension TableViewController { + static let bottomInset: CGFloat = .newStatusButtonDimension + .defaultSpacing * 4 + var visibleLoadMoreViews: [LoadMoreView] { tableView.visibleCells.compactMap { $0.contentView as? LoadMoreView } } @@ -291,7 +294,7 @@ private extension TableViewController { let indexPath = self.dataSource.indexPath(itemId: itemId) { if self.viewModel.shouldAdjustContentInset { self.tableView.contentInset.bottom = max( - 0, + Self.bottomInset, self.tableView.frame.height - self.tableView.contentSize.height - self.tableView.safeAreaInsets.top diff --git a/Views/TabNavigationView.swift b/Views/TabNavigationView.swift index 07660da..78e00a3 100644 --- a/Views/TabNavigationView.swift +++ b/Views/TabNavigationView.swift @@ -61,9 +61,6 @@ struct TabNavigationView: View { } private extension TabNavigationView { - static let newStatusButtonDimension: CGFloat = 54 - static let newStatusButtonShadowRadius: CGFloat = 2 - @ViewBuilder var pendingView: some View { NavigationView { @@ -172,17 +169,16 @@ private extension TabNavigationView { Button { viewModel.presentingNewStatus = true } label: { - ZStack { - Circle() + VisualEffectBlur(vibrancyStyle: .label) { Image(systemName: "pencil") .resizable() - .frame( - width: Self.newStatusButtonDimension / 2, - height: Self.newStatusButtonDimension / 2) - .accentColor(.white) + .frame(width: .newStatusButtonDimension / 2, + height: .newStatusButtonDimension / 2) } - .frame(width: Self.newStatusButtonDimension, height: Self.newStatusButtonDimension) - .shadow(radius: Self.newStatusButtonShadowRadius) + .clipShape(Circle()) + .frame(width: .newStatusButtonDimension, + height: .newStatusButtonDimension) + .shadow(radius: .newStatusButtonShadowRadius) .padding() } } diff --git a/Views/ViewConstants.swift b/Views/ViewConstants.swift index ef0a18f..9cb394a 100644 --- a/Views/ViewConstants.swift +++ b/Views/ViewConstants.swift @@ -11,6 +11,8 @@ extension CGFloat { static let hairline = 1 / UIScreen.main.scale static let minimumButtonDimension: Self = 44 static let barButtonItemDimension: Self = 28 + static let newStatusButtonDimension: CGFloat = 54 + static let newStatusButtonShadowRadius: CGFloat = 2 } extension TimeInterval { diff --git a/Views/VisualEffectBlur.swift b/Views/VisualEffectBlur.swift new file mode 100644 index 0000000..4009a8b --- /dev/null +++ b/Views/VisualEffectBlur.swift @@ -0,0 +1,107 @@ +// Copyright © 2020 Metabolist. All rights reserved. + +// Taken from Apple's Fruta example code + +import SwiftUI + +struct VisualEffectBlur: View { + var blurStyle: UIBlurEffect.Style + var vibrancyStyle: UIVibrancyEffectStyle? + var content: Content + + init(blurStyle: UIBlurEffect.Style = .systemMaterial, + vibrancyStyle: UIVibrancyEffectStyle? = nil, + @ViewBuilder content: () -> Content) { + self.blurStyle = blurStyle + self.vibrancyStyle = vibrancyStyle + self.content = content() + } + + var body: some View { + Representable( + blurStyle: blurStyle, + vibrancyStyle: vibrancyStyle, + content: ZStack { content }) + .accessibility(hidden: Content.self == EmptyView.self) + } +} + +extension VisualEffectBlur { + struct Representable: UIViewRepresentable { + var blurStyle: UIBlurEffect.Style + var vibrancyStyle: UIVibrancyEffectStyle? + var content: Content + + func makeUIView(context: Context) -> UIVisualEffectView { + context.coordinator.blurView + } + + func updateUIView(_ view: UIVisualEffectView, context: Context) { + context.coordinator.update(content: content, blurStyle: blurStyle, vibrancyStyle: vibrancyStyle) + } + + func makeCoordinator() -> Coordinator { + Coordinator(content: content) + } + } +} + +extension VisualEffectBlur.Representable { + class Coordinator { + let blurView = UIVisualEffectView() + let vibrancyView = UIVisualEffectView() + let hostingController: UIHostingController + + init(content: Content) { + hostingController = UIHostingController(rootView: content) + hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + hostingController.view.backgroundColor = nil + blurView.contentView.addSubview(vibrancyView) + blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + vibrancyView.contentView.addSubview(hostingController.view) + vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + } + + func update(content: Content, blurStyle: UIBlurEffect.Style, vibrancyStyle: UIVibrancyEffectStyle?) { + hostingController.rootView = content + + let blurEffect = UIBlurEffect(style: blurStyle) + + blurView.effect = blurEffect + + if let vibrancyStyle = vibrancyStyle { + vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect, style: vibrancyStyle) + } else { + vibrancyView.effect = nil + } + + hostingController.view.setNeedsDisplay() + } + } +} + +extension VisualEffectBlur where Content == EmptyView { + init(blurStyle: UIBlurEffect.Style = .systemMaterial) { + self.init(blurStyle: blurStyle, vibrancyStyle: nil) { + EmptyView() + } + } +} + +struct VisualEffectBlur_Previews: PreviewProvider { + static var previews: some View { + ZStack { + LinearGradient( + gradient: Gradient(colors: [.red, .blue]), + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + VisualEffectBlur(blurStyle: .systemUltraThinMaterial, vibrancyStyle: .fill) { + Text("Hello World!") + .frame(width: 200, height: 100) + } + } + .previewLayout(.sizeThatFits) + } +}