1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00

iOS Now Playing

This commit is contained in:
Daniel Barber 2021-02-14 09:13:19 -05:00
parent a1ffd79d21
commit 88ec05999e
42 changed files with 531 additions and 215 deletions

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E411C26C241C10F0008B9682"
BuildableName = "Persephone.app"
BlueprintName = "Persephone-iOS"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E411C281241C10F5008B9682"
BuildableName = "Persephone-iOSTests.xctest"
BlueprintName = "Persephone-iOSTests"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E411C28C241C10F5008B9682"
BuildableName = "Persephone-iOSUITests.xctest"
BlueprintName = "Persephone-iOSUITests"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E411C26C241C10F0008B9682"
BuildableName = "Persephone.app"
BlueprintName = "Persephone-iOS"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E411C26C241C10F0008B9682"
BuildableName = "Persephone.app"
BlueprintName = "Persephone-iOS"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "nextTrackButtonHugeiOS.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "pauseButtonHugeiOS.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "playButtonHugeiOS.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "prevTrackButtonHugeiOS.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "speakerHigh.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

Binary file not shown.

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "speakerOff.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

Binary file not shown.

View File

@ -34,6 +34,7 @@ class AlbumViewController: UICollectionViewController {
}
@objc func didConnect() {
print("Album view controller - Connected!")
App.mpdClient.fetchAllAlbums()
}

View File

@ -0,0 +1,134 @@
//
// NowPlayingViewController.swift
// Persephone-iOS
//
// Created by Dan Barber on 2020-5-15.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import UIKit
import ReSwift
import Kingfisher
class NowPlayingBarViewController: UIViewController {
@IBOutlet var separatorHeight: NSLayoutConstraint!
@IBOutlet var playPauseButton: UIButton!
@IBOutlet var nextButton: UIButton!
@IBOutlet var songTitle: UILabel!
@IBOutlet var albumCoverView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
separatorHeight.constant = 1 / traitCollection.displayScale
App.store.subscribe(self) {
$0.select {
($0.playerState, $0.queueState)
}
}
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
albumCoverView.layer.backgroundColor = UIColor.black.cgColor
albumCoverView.layer.cornerRadius = 4
albumCoverView.layer.borderWidth = 1 / traitCollection.displayScale
albumCoverView.layer.masksToBounds = true
setAppearance()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
separatorHeight.constant = 1 / traitCollection.displayScale
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
setAppearance()
}
}
func setAppearance() {
let darkMode = traitCollection.userInterfaceStyle == .dark
albumCoverView.layer.borderColor = darkMode ? CGColor.albumBorderColorDark : CGColor.albumBorderColorLight
}
@objc func didConnect() {
App.mpdClient.fetchQueue()
}
@objc func willDisconnect() {
DispatchQueue.main.async {
App.store.dispatch(UpdateQueuePosAction(queuePos: -1))
App.store.dispatch(UpdateQueueAction(queue: []))
}
}
@objc func didReloadAlbumArt() {
// NO-OP
}
func setTransportControlState(_ state: PlayerState) {
guard let state = state.state else { return }
playPauseButton.isEnabled = state.isOneOf([.playing, .paused, .stopped])
nextButton.isEnabled = state.isOneOf([.playing, .paused])
if state.isOneOf([.paused, .stopped, .unknown]) {
playPauseButton.setImage(.playIconLarge, for: .normal)
} else {
playPauseButton.setImage(.pauseIconLarge, for: .normal)
}
}
func setSong(_ song: Song?) {
guard let song = song else {
self.songTitle.text = "Not Playing"
self.albumCoverView.image = .defaultCoverArt
return
}
songTitle.text = song.title
let provider = MPDAlbumArtImageDataProvider(
songUri: song.mpdSong.uriString,
cacheKey: song.album.hash
)
albumCoverView.kf.setImage(
with: .provider(provider),
placeholder: UIImage.defaultCoverArt,
options: [
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
.scaleFactor(traitCollection.displayScale),
]
)
}
@IBAction func playPauseButtonAction(_ sender: Any) {
App.mpdClient.playPause()
}
@IBAction func nextButtonAction(_ sender: Any) {
App.mpdClient.nextTrack()
}
@IBAction func expandNowPlaying(_ sender: Any) {
}
}
extension NowPlayingBarViewController: StoreSubscriber {
typealias StoreSubscriberStateType = (
playerState: PlayerState, queueState: QueueState
)
func newState(state: StoreSubscriberStateType) {
DispatchQueue.main.async {
self.setTransportControlState(state.playerState)
self.setSong(state.playerState.currentSong)
}
}
}

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NowPlayingBarViewController" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="albumCoverView" destination="6Cn-Qu-3ab" id="w0j-pd-zrj"/>
<outlet property="nextButton" destination="k9u-Hf-Cp0" id="bs9-R1-q5h"/>
<outlet property="playPauseButton" destination="aJD-nr-kHS" id="6sM-tz-FHK"/>
<outlet property="separatorHeight" destination="chi-7m-8Ds" id="Ex2-Wm-ygG"/>
<outlet property="songTitle" destination="4nQ-FU-7Ub" id="eGu-nR-CuK"/>
<outlet property="view" destination="fma-vB-sY1" id="5BF-fd-Kur"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" id="fma-vB-sY1">
<rect key="frame" x="0.0" y="0.0" width="335" height="64"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="9j9-AN-ijy">
<rect key="frame" x="0.0" y="0.0" width="335" height="63"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultCoverArt" translatesAutoresizingMaskIntoConstraints="NO" id="6Cn-Qu-3ab">
<rect key="frame" x="20" y="8" width="47" height="47"/>
<constraints>
<constraint firstAttribute="width" secondItem="6Cn-Qu-3ab" secondAttribute="height" multiplier="1:1" id="I6V-Kn-Cwa"/>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tik-7x-PaQ">
<rect key="frame" x="75" y="8" width="0.0" height="47"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" id="DuG-NI-0Bp"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Not Playing" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4nQ-FU-7Ub">
<rect key="frame" x="83" y="8" width="152" height="47"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aJD-nr-kHS" userLabel="PlayButton">
<rect key="frame" x="243" y="8" width="32" height="47"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="4Un-9M-vOk"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="32" id="MMT-BK-4vu"/>
</constraints>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" image="playButtonLarge">
<color key="titleColor" systemColor="labelColor"/>
</state>
<connections>
<action selector="playPauseButtonAction:" destination="-1" eventType="touchUpInside" id="a3n-1h-p00"/>
</connections>
</button>
<button opaque="NO" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k9u-Hf-Cp0" userLabel="NextButton">
<rect key="frame" x="283" y="8" width="32" height="47"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="32" id="etc-kL-NdJ"/>
<constraint firstAttribute="width" constant="32" id="fy0-Z2-OSg"/>
</constraints>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" image="nextTrackButtonLarge">
<color key="titleColor" systemColor="labelColor"/>
</state>
<connections>
<action selector="nextButtonAction:" destination="-1" eventType="touchUpInside" id="3Ke-PH-ud9"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="Xfd-VS-LUl"/>
<directionalEdgeInsets key="directionalLayoutMargins" top="8" leading="20" bottom="8" trailing="20"/>
</stackView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uXQ-Mb-Jkv" userLabel="ExpandButton">
<rect key="frame" x="20" y="8" width="215" height="47"/>
<state key="normal">
<color key="titleColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="expandNowPlaying:" destination="-1" eventType="touchUpInside" id="F2I-ho-gVx"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2EO-qL-ast">
<rect key="frame" x="0.0" y="63" width="335" height="1"/>
<color key="backgroundColor" systemColor="separatorColor"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="chi-7m-8Ds"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="uXQ-Mb-Jkv" firstAttribute="bottom" secondItem="6Cn-Qu-3ab" secondAttribute="bottom" id="9FW-TH-Tef"/>
<constraint firstItem="9j9-AN-ijy" firstAttribute="top" secondItem="fma-vB-sY1" secondAttribute="top" id="Bpq-6d-BwN"/>
<constraint firstItem="uXQ-Mb-Jkv" firstAttribute="trailing" secondItem="4nQ-FU-7Ub" secondAttribute="trailing" id="SI9-AZ-DKb"/>
<constraint firstItem="uXQ-Mb-Jkv" firstAttribute="leading" secondItem="6Cn-Qu-3ab" secondAttribute="leading" id="Wae-93-MpQ"/>
<constraint firstItem="9j9-AN-ijy" firstAttribute="leading" secondItem="fma-vB-sY1" secondAttribute="leading" id="aG7-qp-jXe"/>
<constraint firstAttribute="trailing" secondItem="9j9-AN-ijy" secondAttribute="trailing" id="cCF-6v-1x0"/>
<constraint firstAttribute="trailing" secondItem="2EO-qL-ast" secondAttribute="trailing" id="dp6-XJ-FaI"/>
<constraint firstItem="2EO-qL-ast" firstAttribute="top" secondItem="9j9-AN-ijy" secondAttribute="bottom" id="k9V-5A-Xt0"/>
<constraint firstItem="uXQ-Mb-Jkv" firstAttribute="top" secondItem="6Cn-Qu-3ab" secondAttribute="top" id="ps9-CX-XSk"/>
<constraint firstItem="2EO-qL-ast" firstAttribute="leading" secondItem="fma-vB-sY1" secondAttribute="leading" id="ueX-jc-IqH"/>
<constraint firstAttribute="bottom" secondItem="2EO-qL-ast" secondAttribute="bottom" id="zCc-Cc-PPh"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-512.31884057971024" y="-32.142857142857139"/>
</view>
</objects>
<resources>
<image name="defaultCoverArt" width="128" height="128"/>
<image name="nextTrackButtonLarge" width="28" height="28"/>
<image name="playButtonLarge" width="22" height="22"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="separatorColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -9,32 +9,22 @@
import UIKit
class NowPlayingTabBarController: UITabBarController {
private var barHeight: NSLayoutConstraint!
let nowPlayingViewController = NowPlayingViewController()
let nowPlayingViewController = NowPlayingBarViewController()
override func viewDidLoad() {
super.viewDidLoad()
addChild(nowPlayingViewController)
let subview = nowPlayingViewController.view!
tabBar.superview?.addSubview(subview)
tabBar.clipsToBounds = false
view.addSubview(subview)
subview.translatesAutoresizingMaskIntoConstraints = false
barHeight = subview.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
subview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
subview.trailingAnchor.constraint(equalTo: view.trailingAnchor),
subview.topAnchor.constraint(equalTo: tabBar.topAnchor),
barHeight,
subview.heightAnchor.constraint(equalToConstant: NowPlayingTabBar.barHeight),
])
nowPlayingViewController.didMove(toParent: self)
additionalSafeAreaInsets.bottom = NowPlayingTabBar.barHeight
}
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
super.preferredContentSizeDidChange(forChildContentContainer: container)
barHeight.constant = container.preferredContentSize.height
}
}

View File

@ -2,132 +2,29 @@
// NowPlayingViewController.swift
// Persephone-iOS
//
// Created by Dan Barber on 2020-5-15.
// Created by Dan Barber on 2020-12-01.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import UIKit
import ReSwift
import Kingfisher
class NowPlayingViewController: UIViewController {
@IBOutlet var separatorHeight: NSLayoutConstraint!
@IBOutlet var playPauseButton: UIButton!
@IBOutlet var nextButton: UIButton!
@IBOutlet var songTitle: UILabel!
@IBOutlet var albumCoverView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
separatorHeight.constant = 1 / traitCollection.displayScale
App.store.subscribe(self) {
$0.select { $0.playerState }
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
albumCoverView.layer.backgroundColor = UIColor.black.cgColor
albumCoverView.layer.cornerRadius = 4
albumCoverView.layer.borderWidth = 1 / traitCollection.displayScale
albumCoverView.layer.masksToBounds = true
setAppearance()
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
/*
// MARK: - Navigation
preferredContentSize.height = NowPlayingTabBar.barHeight
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
separatorHeight.constant = 1 / traitCollection.displayScale
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
setAppearance()
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
}
func setAppearance() {
let darkMode = traitCollection.userInterfaceStyle == .dark
*/
albumCoverView.layer.borderColor = darkMode ? CGColor.albumBorderColorDark : CGColor.albumBorderColorLight
}
@objc func didConnect() {
App.mpdClient.fetchQueue()
}
@objc func willDisconnect() {
DispatchQueue.main.async {
App.store.dispatch(UpdateQueuePosAction(queuePos: -1))
App.store.dispatch(UpdateQueueAction(queue: []))
}
}
@objc func didReloadAlbumArt() {
// NO-OP
}
func setTransportControlState(_ state: PlayerState) {
guard let state = state.state else { return }
playPauseButton.isEnabled = state.isOneOf([.playing, .paused, .stopped])
nextButton.isEnabled = state.isOneOf([.playing, .paused])
if state.isOneOf([.paused, .stopped, .unknown]) {
playPauseButton.setImage(.playIconLarge, for: .normal)
} else {
playPauseButton.setImage(.pauseIconLarge, for: .normal)
}
}
func setSong(_ song: Song?) {
guard let song = song else {
self.songTitle.text = "Not Playing"
self.albumCoverView.image = .defaultCoverArt
return
}
songTitle.text = song.title
let provider = MPDAlbumArtImageDataProvider(
songUri: song.mpdSong.uriString,
cacheKey: song.album.hash
)
albumCoverView.kf.setImage(
with: .provider(provider),
placeholder: UIImage.defaultCoverArt,
options: [
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
.scaleFactor(traitCollection.displayScale),
]
)
}
@IBAction func playPauseButtonAction(_ sender: Any) {
App.mpdClient.playPause()
}
@IBAction func nextButtonAction(_ sender: Any) {
App.mpdClient.nextTrack()
}
}
extension NowPlayingViewController: StoreSubscriber {
typealias StoreSubscriberStateType = PlayerState
func newState(state: PlayerState) {
DispatchQueue.main.async {
self.setTransportControlState(state)
self.setSong(state.currentSong)
}
}
}

View File

@ -1,105 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina5_9" orientation="portrait" appearance="dark"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NowPlayingViewController" customModule="Persephone" customModuleProvider="target">
<connections>
<outlet property="albumCoverView" destination="6Cn-Qu-3ab" id="w0j-pd-zrj"/>
<outlet property="nextButton" destination="k9u-Hf-Cp0" id="bs9-R1-q5h"/>
<outlet property="playPauseButton" destination="aJD-nr-kHS" id="6sM-tz-FHK"/>
<outlet property="separatorHeight" destination="chi-7m-8Ds" id="Ex2-Wm-ygG"/>
<outlet property="songTitle" destination="4nQ-FU-7Ub" id="eGu-nR-CuK"/>
<outlet property="view" destination="fma-vB-sY1" id="5BF-fd-Kur"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" id="fma-vB-sY1">
<rect key="frame" x="0.0" y="0.0" width="335" height="64"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="9j9-AN-ijy">
<rect key="frame" x="0.0" y="0.0" width="335" height="63"/>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" minimumValueImage="speakerOff" maximumValueImage="speakerHigh" translatesAutoresizingMaskIntoConstraints="NO" id="69b-PX-D8O">
<rect key="frame" x="30" y="694" width="315" height="31"/>
</slider>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" ambiguous="YES" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="s9e-Zd-M6C">
<rect key="frame" x="32" y="544" width="311" height="4"/>
</progressView>
<stackView opaque="NO" contentMode="center" fixedFrame="YES" distribution="equalCentering" alignment="center" spacing="87" translatesAutoresizingMaskIntoConstraints="NO" id="g2E-QV-g22">
<rect key="frame" x="32" y="601" width="312" height="46"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="defaultCoverArt" translatesAutoresizingMaskIntoConstraints="NO" id="6Cn-Qu-3ab">
<rect key="frame" x="20" y="8" width="47" height="47"/>
<constraints>
<constraint firstAttribute="width" secondItem="6Cn-Qu-3ab" secondAttribute="height" multiplier="1:1" id="I6V-Kn-Cwa"/>
</constraints>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tik-7x-PaQ">
<rect key="frame" x="75" y="8" width="0.0" height="47"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="width" id="DuG-NI-0Bp"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" text="Not Playing" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4nQ-FU-7Ub">
<rect key="frame" x="83" y="8" width="152" height="47"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aJD-nr-kHS">
<rect key="frame" x="243" y="8" width="32" height="47"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="4Un-9M-vOk"/>
</constraints>
<color key="tintColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<state key="normal" image="playButtonLarge">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="playPauseButtonAction:" destination="-1" eventType="touchUpInside" id="a3n-1h-p00"/>
</connections>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ix3-hb-WwP" userLabel="Previous Button">
<rect key="frame" x="0.0" y="0.0" width="46" height="46"/>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" image="prevTrackButtonHugeiOS"/>
</button>
<button opaque="NO" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k9u-Hf-Cp0">
<rect key="frame" x="283" y="8" width="32" height="47"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="fy0-Z2-OSg"/>
</constraints>
<color key="tintColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<state key="normal" image="nextTrackButtonLarge">
<color key="titleColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="nextButtonAction:" destination="-1" eventType="touchUpInside" id="3Ke-PH-ud9"/>
</connections>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4qp-Jh-zT7" userLabel="Play Button">
<rect key="frame" x="133" y="0.0" width="46" height="46"/>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" image="playButtonHugeiOS"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5SE-nt-QeW" userLabel="Next Button">
<rect key="frame" x="266" y="0.0" width="46" height="46"/>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" image="nextTrackButtonHugeiOS"/>
</button>
</subviews>
<directionalEdgeInsets key="directionalLayoutMargins" top="8" leading="20" bottom="8" trailing="20"/>
<viewLayoutGuide key="safeArea" id="Xfd-VS-LUl"/>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2EO-qL-ast">
<rect key="frame" x="0.0" y="63" width="335" height="1"/>
<color key="backgroundColor" systemColor="separatorColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="chi-7m-8Ds"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="9j9-AN-ijy" firstAttribute="top" secondItem="fma-vB-sY1" secondAttribute="top" id="Bpq-6d-BwN"/>
<constraint firstItem="9j9-AN-ijy" firstAttribute="leading" secondItem="fma-vB-sY1" secondAttribute="leading" id="aG7-qp-jXe"/>
<constraint firstAttribute="trailing" secondItem="9j9-AN-ijy" secondAttribute="trailing" id="cCF-6v-1x0"/>
<constraint firstAttribute="trailing" secondItem="2EO-qL-ast" secondAttribute="trailing" id="dp6-XJ-FaI"/>
<constraint firstItem="2EO-qL-ast" firstAttribute="top" secondItem="9j9-AN-ijy" secondAttribute="bottom" id="k9V-5A-Xt0"/>
<constraint firstItem="2EO-qL-ast" firstAttribute="leading" secondItem="fma-vB-sY1" secondAttribute="leading" id="ueX-jc-IqH"/>
<constraint firstAttribute="bottom" secondItem="2EO-qL-ast" secondAttribute="bottom" id="zCc-Cc-PPh"/>
<constraint firstItem="s9e-Zd-M6C" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="32" id="OzT-yE-YIZ"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="69b-PX-D8O" secondAttribute="bottom" constant="54" id="S4t-SJ-XGN"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="s9e-Zd-M6C" secondAttribute="trailing" constant="32" id="TmT-dF-PSV"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="69b-PX-D8O" secondAttribute="trailing" constant="32" id="b4J-ZE-ZiR"/>
<constraint firstItem="69b-PX-D8O" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" constant="32" id="du2-8n-Nmm"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-512.31884057971024" y="-32.142857142857139"/>
<point key="canvasLocation" x="-376.80000000000001" y="73.152709359605907"/>
</view>
</objects>
<resources>
<image name="defaultCoverArt" width="128" height="128"/>
<image name="nextTrackButtonLarge" width="28" height="28"/>
<image name="playButtonLarge" width="22" height="22"/>
<image name="nextTrackButtonHugeiOS" width="46" height="46"/>
<image name="playButtonHugeiOS" width="46" height="46"/>
<image name="prevTrackButtonHugeiOS" width="46" height="46"/>
<image name="speakerHigh" width="21" height="17"/>
<image name="speakerOff" width="21" height="17"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -21,7 +21,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
_ = App.mpdServerController
App.store.dispatch(UpdateServerHost(host: "192.168.1.42"))
App.store.dispatch(UpdateServerHost(host: "192.168.4.31"))
}
func sceneDidDisconnect(_ scene: UIScene) {