Skip to content

Commit

Permalink
library screen, handle swipes, library tabs, bind tab indicator anima…
Browse files Browse the repository at this point in the history
…tion to swipes and touches
  • Loading branch information
Auridel committed Dec 29, 2021
1 parent dca1390 commit f6ea940
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 1 deletion.
28 changes: 28 additions & 0 deletions Swift Spotify Clone.xcodeproj/project.pbxproj
Expand Up @@ -66,6 +66,9 @@
F8E2D6F12777B3EC005FEE1E /* TitleHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E2D6F02777B3EC005FEE1E /* TitleHeaderCollectionReusableView.swift */; };
F8E2D6F42777BC4D005FEE1E /* AlbumTrackCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E2D6F32777BC4D005FEE1E /* AlbumTrackCollectionViewCell.swift */; };
F8E2D6F62777BD7B005FEE1E /* AlbumCollectionViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E2D6F52777BD7B005FEE1E /* AlbumCollectionViewCellViewModel.swift */; };
F8E7C019277CE93B001C4E50 /* LibraryPlaylistsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E7C018277CE93B001C4E50 /* LibraryPlaylistsViewController.swift */; };
F8E7C01B277CE95E001C4E50 /* LibraryAlbumsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E7C01A277CE95E001C4E50 /* LibraryAlbumsViewController.swift */; };
F8E7C01E277CED06001C4E50 /* LibraryToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E7C01D277CED06001C4E50 /* LibraryToggleView.swift */; };
F8EDC8E22770FB980027E263 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EDC8E12770FB980027E263 /* Keys.swift */; };
F8EDC8E42770FF3D0027E263 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EDC8E32770FF3D0027E263 /* Extensions.swift */; };
F8EDC8E6277116C20027E263 /* AuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EDC8E5277116C20027E263 /* AuthResponse.swift */; };
Expand Down Expand Up @@ -135,6 +138,9 @@
F8E2D6F02777B3EC005FEE1E /* TitleHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleHeaderCollectionReusableView.swift; sourceTree = "<group>"; };
F8E2D6F32777BC4D005FEE1E /* AlbumTrackCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumTrackCollectionViewCell.swift; sourceTree = "<group>"; };
F8E2D6F52777BD7B005FEE1E /* AlbumCollectionViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumCollectionViewCellViewModel.swift; sourceTree = "<group>"; };
F8E7C018277CE93B001C4E50 /* LibraryPlaylistsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryPlaylistsViewController.swift; sourceTree = "<group>"; };
F8E7C01A277CE95E001C4E50 /* LibraryAlbumsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryAlbumsViewController.swift; sourceTree = "<group>"; };
F8E7C01D277CED06001C4E50 /* LibraryToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryToggleView.swift; sourceTree = "<group>"; };
F8EDC8E12770FB980027E263 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
F8EDC8E32770FF3D0027E263 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
F8EDC8E5277116C20027E263 /* AuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthResponse.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -216,6 +222,7 @@
F813B951276FD4840077216F /* Views */ = {
isa = PBXGroup;
children = (
F8E7C01C277CECE8001C4E50 /* Library */,
F818ED24277BA35900261A35 /* Player */,
F8469713277A5FD9007A84B2 /* SearchResults */,
F8E099292778EE7500CF7E18 /* Search */,
Expand Down Expand Up @@ -311,6 +318,7 @@
F813B95E276FD6680077216F /* Other */ = {
isa = PBXGroup;
children = (
F8E7C017277CE918001C4E50 /* Library */,
F813B95F276FD67A0077216F /* AuthViewController.swift */,
F813B961276FD6870077216F /* WelcomeViewController.swift */,
F813B963276FD6950077216F /* SettingsViewController.swift */,
Expand Down Expand Up @@ -392,6 +400,23 @@
path = Album;
sourceTree = "<group>";
};
F8E7C017277CE918001C4E50 /* Library */ = {
isa = PBXGroup;
children = (
F8E7C018277CE93B001C4E50 /* LibraryPlaylistsViewController.swift */,
F8E7C01A277CE95E001C4E50 /* LibraryAlbumsViewController.swift */,
);
path = Library;
sourceTree = "<group>";
};
F8E7C01C277CECE8001C4E50 /* Library */ = {
isa = PBXGroup;
children = (
F8E7C01D277CED06001C4E50 /* LibraryToggleView.swift */,
);
path = Library;
sourceTree = "<group>";
};
F8EDC8E02770FB8F0027E263 /* Keys */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -546,13 +571,15 @@
F8054AE62773C6AD00AC1DCC /* NewReleasesResponse.swift in Sources */,
F813B966276FD6A00077216F /* ProfileViewController.swift in Sources */,
F813B958276FD6120077216F /* TabBarViewController.swift in Sources */,
F8E7C019277CE93B001C4E50 /* LibraryPlaylistsViewController.swift in Sources */,
F813B972276FD7170077216F /* HapticsManager.swift in Sources */,
F8EDC8E42770FF3D0027E263 /* Extensions.swift in Sources */,
F8469715277A600B007A84B2 /* SearchResultDefaultTableViewCell.swift in Sources */,
F83687FE27776DE4005DDF43 /* PlaylistHeaderCollectionReusableView.swift in Sources */,
F813B976276FD7400077216F /* AudioTrack.swift in Sources */,
F813B941276FD38D0077216F /* HomeViewController.swift in Sources */,
F813B970276FD7090077216F /* ApiManager.swift in Sources */,
F8E7C01B277CE95E001C4E50 /* LibraryAlbumsViewController.swift in Sources */,
F813B960276FD67A0077216F /* AuthViewController.swift in Sources */,
F864A70C2775251A00A362BE /* FeaturedPlaylistCollectionViewCell.swift in Sources */,
F813B97A276FD7590077216F /* UserProfile.swift in Sources */,
Expand All @@ -572,6 +599,7 @@
F831D4642776540000E6E092 /* Enums.swift in Sources */,
F813B964276FD6950077216F /* SettingsViewController.swift in Sources */,
F846971B277A6517007A84B2 /* SearchResultsSubtitleTableViewCellViewModel.swift in Sources */,
F8E7C01E277CED06001C4E50 /* LibraryToggleView.swift in Sources */,
F818ED28277BB15F00261A35 /* PlayerControlsViewViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
97 changes: 97 additions & 0 deletions Swift Spotify Clone/Controllers/Core/LibraryViewController.swift
Expand Up @@ -8,12 +8,109 @@
import UIKit

class LibraryViewController: UIViewController {

private let playlistsVC = LibraryPlaylistsViewController()

private let albumsVC = LibraryAlbumsViewController()

private let toggleView = LibraryToggleView()

private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
return scrollView
}()

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .systemBackground

configureViews()
addChildren()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

scrollView.frame = CGRect(
x: 0,
y: view.safeAreaInsets.top + 55,
width: view.width,
height: view.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom - 55)
toggleView.frame = CGRect(
x: 0,
y: view.safeAreaInsets.top,
width: 200,
height: 55)
}

// MARK: Common

private func configureViews() {
scrollView.delegate = self
toggleView.delegate = self

view.addSubview(scrollView)
view.addSubview(toggleView)

scrollView.contentSize = CGSize(
width: view.width * 2,
height: scrollView.height)


}

private func addChildren() {
addChild(playlistsVC)
scrollView.addSubview(playlistsVC.view)
playlistsVC.view.frame = CGRect(
x: 0,
y: 0,
width: scrollView.width,
height: scrollView.height)
playlistsVC.didMove(toParent: self)

addChild(albumsVC)
scrollView.addSubview(albumsVC.view)
albumsVC.view.frame = CGRect(
x: view.width,
y: 0,
width: scrollView.width,
height: scrollView.height)
albumsVC.didMove(toParent: self)
}
}


// MARK: ScrollView Delegate

extension LibraryViewController: UIScrollViewDelegate {

func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x >= view.width - 100 {
toggleView.updateIndicator(for: .albums)
} else if scrollView.contentOffset.x <= 100 {
toggleView.updateIndicator(for: .playlists)
}
}
}

// MARK: ToggleView Delegate

extension LibraryViewController: LibraryToggleViewDelegate {

func libraryToggleViewDidTapPlaylists() {
scrollView.setContentOffset(
.zero,
animated: true)
}

func libraryToggleViewDidTapAlbums() {
scrollView.setContentOffset(
CGPoint(x: view.width, y: 0),
animated: true)
}


}
@@ -0,0 +1,18 @@
//
// LibraryAlbumsViewController.swift
// Swift Spotify Clone
//
// Created by Олег Ефимов on 29.12.2021.
//

import UIKit

class LibraryAlbumsViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .systemBlue
}

}
@@ -0,0 +1,17 @@
//
// LibraryPlaylistsViewController.swift
// Swift Spotify Clone
//
// Created by Олег Ефимов on 29.12.2021.
//

import UIKit

class LibraryPlaylistsViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .systemPink
}
}
4 changes: 3 additions & 1 deletion Swift Spotify Clone/Presenter/PlaybackPresenter.swift
Expand Up @@ -35,8 +35,10 @@ final class PlaybackPresenter {
private var playerQueue: AVQueuePlayer?

private var currentTrack: AudioTrack? {
if currentIndex < tracks.count {
if !tracks.isEmpty, currentIndex < tracks.count {
return tracks[currentIndex]
} else if track != nil, tracks.isEmpty {
return track
}
return nil
}
Expand Down
130 changes: 130 additions & 0 deletions Swift Spotify Clone/Views/Library/LibraryToggleView.swift
@@ -0,0 +1,130 @@
//
// LibraryToggleView.swift
// Swift Spotify Clone
//
// Created by Олег Ефимов on 29.12.2021.
//

import UIKit

protocol LibraryToggleViewDelegate: AnyObject {
func libraryToggleViewDidTapPlaylists()
func libraryToggleViewDidTapAlbums()
}

class LibraryToggleView: UIView {

enum LibraryToggleState {
case playlists, albums
}

weak var delegate: LibraryToggleViewDelegate?

private var state: LibraryToggleState = .playlists

private let playlistButton: UIButton = {
let button = UIButton()
button.setTitleColor(.label,
for: .normal)
button.setTitle("Playlists",
for: .normal)
return button
}()

private let albumsButton: UIButton = {
let button = UIButton()
button.setTitleColor(.label,
for: .normal)
button.setTitle("Albums",
for: .normal)
return button
}()

private let indicatorView: UIView = {
let view = UIView()
view.backgroundColor = .systemGreen
view.layer.masksToBounds = true
view.layer.cornerRadius = 4
return view
}()

override init(frame: CGRect) {
super.init(frame: frame)

playlistButton.addTarget(self,
action: #selector(didTapPlaylists),
for: .touchUpInside)
albumsButton.addTarget(self,
action: #selector(didTapAlbums),
for: .touchUpInside)

addSubview(playlistButton)
addSubview(albumsButton)
addSubview(indicatorView)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {
super.layoutSubviews()

playlistButton.frame = CGRect(
x: 0,
y: 0,
width: 100,
height: 40)
albumsButton.frame = CGRect(
x: playlistButton.right,
y: 0,
width: 100,
height: 40)
layoutIndicator()
}

// MARK: Action

@objc private func didTapPlaylists() {
state = .playlists
animateIndicator()
delegate?.libraryToggleViewDidTapPlaylists()
}

@objc private func didTapAlbums() {
state = .albums
animateIndicator()
delegate?.libraryToggleViewDidTapAlbums()
}

// MARK: Common

public func updateIndicator(for indicatorState: LibraryToggleState) {
state = indicatorState
animateIndicator()
}

private func animateIndicator() {
UIView.animate(withDuration: 0.2) { [weak self] in
self?.layoutIndicator()
}
}

private func layoutIndicator() {
switch state {
case .playlists:
indicatorView.frame = CGRect(
x: 0,
y: playlistButton.bottom,
width: 100,
height: 3)
case .albums:
indicatorView.frame = CGRect(
x: 100,
y: playlistButton.bottom,
width: 100,
height: 3)
}
}

}

0 comments on commit f6ea940

Please sign in to comment.