diff --git a/global/MediaControlsViewController.swift b/global/MediaControlsViewController.swift index d5bb4da..5e83a9f 100644 --- a/global/MediaControlsViewController.swift +++ b/global/MediaControlsViewController.swift @@ -8,35 +8,22 @@ class MediaControlsViewController: UIViewController, MPUNowPlayingDelegate { case notification } - // we need to keep strong references around to these, otherwise they’d be released by ARC and we + // we need to keep strong references to these around, otherwise they’d be released by ARC and we // don’t get to have the goodness they provide var nowPlayingController: MPUNowPlayingController! var controlsViewController: HaxMediaControlsViewController! + var containerView: UIView! var artworkView: MPUNowPlayingArtworkView! var labelsContainerView: UIView! - var appNameLabel: UILabel! var titleLabel: MPUControlCenterMetadataView! - var albumLabel: MPUControlCenterMetadataView! - var artistLabel: MPUControlCenterMetadataView! var artistAlbumLabel: MPUControlCenterMetadataView! var transportControls: UIView! - var extrasView: UIView! - var timeView: UIView! - var extrasSpacerView: UIView! - - var appNameLabelHeightConstraint: NSLayoutConstraint! var titleLabelHeightConstraint: NSLayoutConstraint! - var albumLabelHeightConstraint: NSLayoutConstraint! - var artistLabelHeightConstraint: NSLayoutConstraint! var artistAlbumLabelHeightConstraint: NSLayoutConstraint! - var containerHeightConstraint: NSLayoutConstraint! - //var expandedTopConstraint: NSLayoutConstraint! - //var collapsedBottomConstraint: NSLayoutConstraint! - var state = InterfaceState.notification { didSet { view.setNeedsLayout() @@ -69,102 +56,57 @@ class MediaControlsViewController: UIViewController, MPUNowPlayingDelegate { let controlsView = controlsViewController.view! // construct the views - let containerView = UIView() + containerView = UIView() containerView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(containerView) artworkView = MPUNowPlayingArtworkView() - artworkView.activated = true artworkView.translatesAutoresizingMaskIntoConstraints = false + artworkView.activated = true containerView.addSubview(artworkView) labelsContainerView = UIView() labelsContainerView.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(labelsContainerView) - // steal the transport controls from MPUControlCenterMediaControlsViewController, which will - // manage them for us - transportControls = controlsView.transportControls - transportControls.translatesAutoresizingMaskIntoConstraints = false - containerView.addSubview(transportControls) - - appNameLabel = UILabel() - appNameLabel.translatesAutoresizingMaskIntoConstraints = false - appNameLabel.font = UIFont.systemFont(ofSize: 15) - appNameLabel.textColor = UIColor(white: 0.9, alpha: 1) - labelsContainerView.addSubview(appNameLabel) - // also steal the labels titleLabel = controlsView.value(forKey: "_titleLabel") as! MPUControlCenterMetadataView titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.numberOfLines = 1 titleLabel.marqueeEnabled = true + titleLabel.isUserInteractionEnabled = false labelsContainerView.addSubview(titleLabel) - - albumLabel = controlsView.value(forKey: "_albumLabel") as! MPUControlCenterMetadataView - albumLabel.translatesAutoresizingMaskIntoConstraints = false - albumLabel.marqueeEnabled = true - labelsContainerView.addSubview(albumLabel) - - artistLabel = controlsView.value(forKey: "_artistLabel") as! MPUControlCenterMetadataView - artistLabel.translatesAutoresizingMaskIntoConstraints = false - artistLabel.marqueeEnabled = true - labelsContainerView.addSubview(artistLabel) artistAlbumLabel = controlsView.value(forKey: "_artistAlbumConcatenatedLabel") as! MPUControlCenterMetadataView artistAlbumLabel.translatesAutoresizingMaskIntoConstraints = false + artistAlbumLabel.numberOfLines = 1 artistAlbumLabel.marqueeEnabled = true + artistAlbumLabel.isUserInteractionEnabled = false labelsContainerView.addSubview(artistAlbumLabel) - appNameLabelHeightConstraint = NSLayoutConstraint(item: appNameLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - titleLabelHeightConstraint = NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - albumLabelHeightConstraint = NSLayoutConstraint(item: albumLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - artistLabelHeightConstraint = NSLayoutConstraint(item: artistLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - artistAlbumLabelHeightConstraint = NSLayoutConstraint(item: artistAlbumLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - - labelsContainerView.addConstraints([ - titleLabelHeightConstraint, - albumLabelHeightConstraint, - artistLabelHeightConstraint, - artistAlbumLabelHeightConstraint - ]) + // steal the transport controls from MPUControlCenterMediaControlsViewController, which will + // manage them for us + transportControls = controlsView.transportControls + transportControls.translatesAutoresizingMaskIntoConstraints = false + labelsContainerView.addSubview(transportControls) // sean spacer (and our prime minister, mr trumble) let spacerView = UIView() + spacerView.translatesAutoresizingMaskIntoConstraints = false labelsContainerView.addSubview(spacerView) - // views within the widget expanded area - if isWidget { - containerHeightConstraint = NSLayoutConstraint(item: containerView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) - view.addConstraint(containerHeightConstraint) - } - - extrasView = UIView() - extrasView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(extrasView) - - timeView = controlsView.value(forKey: "_timeView") as! UIView - timeView.translatesAutoresizingMaskIntoConstraints = false - extrasView.addSubview(timeView) - - extrasSpacerView = UIView() - extrasSpacerView.translatesAutoresizingMaskIntoConstraints = false - extrasView.addSubview(extrasSpacerView) - // do that auto layout stuff - let margin: CGFloat = isWidget ? 11 : 14 + let widgetHeight: CGFloat = 95 + let margin: CGFloat = isWidget ? 6 : 12 + let artworkInset: CGFloat = isWidget ? 4 : 2 let metrics: [String: NSNumber] = [ - "heightFactor": 0.4, - "widgetHeight": 95, + "widgetHeight": widgetHeight as NSNumber, + "widgetArtworkSize": (widgetHeight - (margin + artworkInset) * 2) as NSNumber, "margin": margin as NSNumber, - "doubleMargin": (margin * 2 as CGFloat) as NSNumber, // swift... - "labelsContainerLeading": isWidget ? (margin - 4) as NSNumber : margin as NSNumber, - "labelsContainerTrailing": isWidget ? (margin - 6) as NSNumber : margin as NSNumber, - "labelsContainerTopOutset": isWidget ? -3 : 0, - "labelsContainerBottomOutset": isWidget ? -6 : 0, - - "labelSpacing": 4, + "artworkInset": artworkInset as NSNumber, + "innerMargin": isWidget ? 6 : 8, "controlsWidth": 160, "controlsHeight": isWidget ? 38 : 44 @@ -176,60 +118,56 @@ class MediaControlsViewController: UIViewController, MPUNowPlayingDelegate { "artworkView": artworkView, "labelsContainerView": labelsContainerView, - "transportControls": transportControls, - - "appNameLabel": appNameLabel, + "titleLabel": titleLabel, - "albumLabel": albumLabel, - "artistLabel": artistLabel, "artistAlbumLabel": artistAlbumLabel, - "spacerView": spacerView + "spacerView": spacerView, + "transportControls": transportControls ] view.hb_addCompactConstraints([ "containerView.top = self.top + margin", "containerView.leading = self.leading + margin", - "containerView.trailing = self.trailing - margin", - - isWidget - ? "containerView.height = widgetHeight - doubleMargin" - : "containerView.bottom = self.bottom - margin" + "containerView.trailing = self.trailing - margin" + ], metrics: metrics, views: views) + + containerView.hb_addCompactConstraints([ + "artworkView.top = containerView.top + artworkInset", + "artworkView.width = artworkView.height" ], metrics: metrics, views: views) if !isWidget { - view.hb_addCompactConstraints([ - "self.height = self.width * heightFactor" + view.hb_addCompactConstraint("containerView.bottom = self.bottom - margin", metrics: metrics, views: views) + + containerView.hb_addCompactConstraints([ + "artworkView.bottom = containerView.bottom - artworkInset" + "labelsContainerView.top = containerView.top", + "labelsContainerView.bottom = containerView.bottom" ], metrics: metrics, views: views) } - - containerView.hb_addCompactConstraints([ - "artworkView.top = self.top", - "artworkView.leading = self.leading", - "artworkView.bottom = self.bottom", - "artworkView.width = self.height", - - "labelsContainerView.top = self.top + labelsContainerTopOutset", - "labelsContainerView.leading = artworkView.trailing + labelsContainerLeading", - "labelsContainerView.trailing = self.trailing + labelsContainerTrailing", - "transportControls.top = labelsContainerView.bottom", - "transportControls.bottom = self.bottom - labelsContainerBottomOutset", - "transportControls.width = controlsWidth", - "transportControls.height = controlsHeight", - "transportControls.centerX = labelsContainerView.centerX" - ], metrics: metrics, views: views) + containerView.hb_addConstraints(withVisualFormat: "H:|-artworkInset-[artworkView\(isWidget ? "(==widgetArtworkSize)" : "")]-innerMargin-[labelsContainerView]|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) // is there not a better, concise way to do this?? labelsContainerView.hb_addCompactConstraints([ - "appNameLabel.leading = self.leading", "appNameLabel.trailing = self.trailing", "titleLabel.leading = self.leading", "titleLabel.trailing = self.trailing", - "albumLabel.leading = self.leading", "albumLabel.trailing = self.trailing", - "artistLabel.leading = self.leading", "artistLabel.trailing = self.trailing", "artistAlbumLabel.leading = self.leading", "artistAlbumLabel.trailing = self.trailing", "spacerView.leading = self.leading", "spacerView.trailing = self.trailing", + + "transportControls.width = controlsWidth", + "transportControls.centerX = labelsContainerView.centerX" ], metrics: metrics, views: views) - labelsContainerView.hb_addConstraints(withVisualFormat: "V:|[appNameLabel][titleLabel][albumLabel][artistLabel][artistAlbumLabel][spacerView]|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) + labelsContainerView.hb_addConstraints(withVisualFormat: "V:|[titleLabel][artistAlbumLabel][spacerView][transportControls(controlsHeight)]|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) + + // manually make some constraints for the label heights + titleLabelHeightConstraint = NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) + artistAlbumLabelHeightConstraint = NSLayoutConstraint(item: artistAlbumLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) + + labelsContainerView.addConstraints([ + titleLabelHeightConstraint, + artistAlbumLabelHeightConstraint + ]) // set up the now playing controller to send us updates nowPlayingController = MPUNowPlayingController() @@ -244,56 +182,30 @@ class MediaControlsViewController: UIViewController, MPUNowPlayingDelegate { super.viewWillLayoutSubviews() + // numberOfLines and marqueeEnabled can be changed behind our backs (i should really just + // construct these labels myself... not enough time now) so set them again here titleLabel.numberOfLines = isSmall ? 1 : 2 - titleLabel.marqueeEnabled = isSmall - - albumLabel.isHidden = isSmall - artistLabel.isHidden = isSmall - artistAlbumLabel.isHidden = !isSmall - extrasView.isHidden = isSmall - - artistAlbumLabel.marqueeEnabled = true + artistAlbumLabel.numberOfLines = isSmall ? 1 : 2 - switch state { - case .widgetCollapsed: - containerHeightConstraint.constant = 95 - break - - case .widgetExpanded: - containerHeightConstraint.constant = 140 - break - - case .notification: - break - } + // if titleLabel isn't 1 line, this won't do anything but will still align it with the others + titleLabel.marqueeEnabled = isSmall + artistAlbumLabel.marqueeEnabled = isSmall + + artistAlbumLabel.isHidden = false let labelExtraMargin: CGFloat = 2 // the fuck is a greatestFiniteMagnitude? why wasn’t calling it “max”, like, you know, the // maximum number possible for the type, good enough? let size = CGSize(width: labelsContainerView.frame.size.width, height: CGFloat.greatestFiniteMagnitude) - appNameLabelHeightConstraint.constant = isSmall ? 0 : appNameLabel.sizeThatFits(size).height + labelExtraMargin titleLabelHeightConstraint.constant = titleLabel.sizeThatFits(size).height + labelExtraMargin - albumLabelHeightConstraint.constant = isSmall ? 0 : albumLabel.sizeThatFits(size).height + labelExtraMargin - artistLabelHeightConstraint.constant = isSmall ? 0 : artistLabel.sizeThatFits(size).height + labelExtraMargin - artistAlbumLabelHeightConstraint.constant = isSmall ? artistAlbumLabel.sizeThatFits(size).height + labelExtraMargin : 0 + artistAlbumLabelHeightConstraint.constant = artistAlbumLabel.sizeThatFits(size).height + labelExtraMargin } // MARK: - Now playing func nowPlayingController(_ nowPlayingController: MPUNowPlayingController!, nowPlayingInfoDidChange info: [AnyHashable: Any]!) { artworkView.artworkImage = nowPlayingController.currentNowPlayingArtwork - - if appNameLabel != nil { - if nowPlayingController.nowPlayingAppDisplayID == nil { - appNameLabel.text = "" - } else { - if let app = LSApplicationProxy(forIdentifier: nowPlayingController.nowPlayingAppDisplayID) { - appNameLabel.text = app.localizedName.localizedUppercase - } - } - } - view.setNeedsLayout() } diff --git a/notification-content/NotificationViewController.swift b/notification-content/NotificationViewController.swift index ee1b362..d338c5f 100644 --- a/notification-content/NotificationViewController.swift +++ b/notification-content/NotificationViewController.swift @@ -16,6 +16,16 @@ class NotificationViewController: MediaControlsViewController, UNNotificationCon fatalError("") } + // MARK: - View controller + + override func loadView() { + super.loadView() + + // ensure the view is the same height as we defined in the Info.plist, so we don't get ugly + // window resizing between when the window opens and when the content is fully loaded + view.hb_addCompactConstraint("self.height = self.width * heightFactor", metrics: [ "heightFactor": 0.4 ], views: nil) + } + // MARK: - Notification content func didReceive(_ notification: UNNotification) { diff --git a/widget/TodayViewController.swift b/widget/TodayViewController.swift index 19fa89d..8ee2c2b 100644 --- a/widget/TodayViewController.swift +++ b/widget/TodayViewController.swift @@ -3,6 +3,10 @@ import NotificationCenter @objc(TodayViewController) class TodayViewController: MediaControlsViewController, NCWidgetProviding { + + var extrasView: UIView! + var timeView: UIView! + var extrasSpacerView: UIView! // MARK: - Init @@ -17,6 +21,46 @@ class TodayViewController: MediaControlsViewController, NCWidgetProviding { // MARK: - View controller + override func loadView() { + super.loadView() + + let controlsView = controlsViewController.view! + + extrasView = UIView() + extrasView.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(extrasView) + + timeView = controlsView.value(forKey: "_timeView") as! UIView + timeView.translatesAutoresizingMaskIntoConstraints = false + extrasView.addSubview(timeView) + + let metrics: [String: NSNumber] = [ + "margin": 6, + "timeInset": 6, + "timeHeight": 60, + ] + + let views: [String: UIView] = [ + "containerView": containerView, + "labelsContainerView": labelsContainerView, + "extrasView": extrasView, + + "timeView": timeView + ] + + containerView.hb_addCompactConstraints([ + "extrasView.top = labelsContainerView.bottom", + "extrasView.bottom = containerView.bottom", + "extrasView.left = containerView.left", + "extrasView.right = containerView.right" + ], metrics: metrics, views: views) + + extrasView.hb_addConstraints(withVisualFormat: "H:|-timeInset-[timeView]-timeInset-|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) + extrasView.hb_addConstraints(withVisualFormat: "V:|[timeView(timeHeight)]|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) + + containerView.hb_addConstraints(withVisualFormat: "V:|[labelsContainerView][extrasView]|", options: NSLayoutFormatOptions(), metrics: metrics, views: views) + } + override func viewDidLoad() { super.viewDidLoad() @@ -24,6 +68,14 @@ class TodayViewController: MediaControlsViewController, NCWidgetProviding { extensionContext!.widgetLargestAvailableDisplayMode = .expanded } + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + let isSmall = self.state == .widgetCollapsed + + extrasView.alpha = isSmall ? 0 : 1 + } + // MARK: - Widget var controlsState: MediaControlsViewController.InterfaceState { @@ -31,7 +83,7 @@ class TodayViewController: MediaControlsViewController, NCWidgetProviding { switch extensionContext!.widgetActiveDisplayMode { case .compact: return .widgetCollapsed - + case .expanded: return .widgetExpanded } @@ -41,9 +93,9 @@ class TodayViewController: MediaControlsViewController, NCWidgetProviding { // set the content size. if our preferred height is larger than the maximum we’re currently // allowed, then we need to just go with the max. this also makes it use ios’s default compact // height when in compact mode (currently, 95pt) - preferredContentSize = CGSize(width: maxSize.width, height: min(maxSize.height, 120)) + preferredContentSize = CGSize(width: maxSize.width, height: min(maxSize.height, 186)) - // set the expanded mode on the controls + // set the expanded mode on the controls (which will also call setNeedsLayout()) state = controlsState }