iOS 10 ha una funzione che vorrei replicare. Quando tocchi un album 3D nell'app Apple Music, si apre il menu mostrato di seguito. Tuttavia a differenza di una normale sbirciatina e pop, non va via quando alzi il dito. Come posso replicare questo?Come faccio a replicare Apple Music di iOS 10 "Menu di azione Peek e pop"
risposta
Il più vicino ho avuto modo di replicare è il seguente codice .. E creare un manichino-replica dell'applicazione Musica .. poi ho aggiunto i delegati PeekPop-3D-Touch.
Tuttavia, nel delegato, aggiungo un osservatore al rilevatore di gesti e quindi annullo il gesto dopo averlo sbirciato ma poi lo riatroo quando il dito è sollevato. Per riattivarlo, l'ho fatto asincrono perché l'anteprima sparirà immediatamente senza la distribuzione asincrona. Non riuscivo a trovare un modo intorno ad esso ..
Ora, se si tocca fuori della scatola blu, sparirà come normale =]
http://i.imgur.com/073M2Ku.jpg http://i.imgur.com/XkwUBly.jpg
//
// ViewController.swift
// PeekPopExample
//
// Created by Brandon Anthony on 2016-07-16.
// Copyright © 2016 XIO. All rights reserved.
//
import UIKit
class MusicViewController: UITabBarController, UITabBarControllerDelegate {
var tableView: UITableView!
var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.initControllers()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func initControllers() {
let libraryController = LibraryViewController()
let forYouController = UIViewController()
let browseController = UIViewController()
let radioController = UIViewController()
let searchController = UIViewController()
libraryController.title = "Library"
libraryController.tabBarItem.image = nil
forYouController.title = "For You"
forYouController.tabBarItem.image = nil
browseController.title = "Browse"
browseController.tabBarItem.image = nil
radioController.title = "Radio"
radioController.tabBarItem.image = nil
searchController.title = "Search"
searchController.tabBarItem.image = nil
self.viewControllers = [libraryController, forYouController, browseController, radioController, searchController];
}
}
E l'implementazione di ForceTouch in pausa ..
//
// LibraryViewController.swift
// PeekPopExample
//
// Created by Brandon Anthony on 2016-07-16.
// Copyright © 2016 XIO. All rights reserved.
//
import Foundation
import UIKit
//Views and Cells..
class AlbumView : UIView {
var albumCover: UIImageView!
var title: UILabel!
var artist: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
self.initControls()
self.setTheme()
self.doLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initControls() {
self.albumCover = UIImageView()
self.title = UILabel()
self.artist = UILabel()
}
func setTheme() {
self.albumCover.contentMode = .scaleAspectFit
self.albumCover.layer.cornerRadius = 5.0
self.albumCover.backgroundColor = UIColor.lightGray()
self.title.text = "Unknown"
self.title.font = UIFont.systemFont(ofSize: 12)
self.artist.text = "Unknown"
self.artist.textColor = UIColor.lightGray()
self.artist.font = UIFont.systemFont(ofSize: 12)
}
func doLayout() {
self.addSubview(self.albumCover)
self.addSubview(self.title)
self.addSubview(self.artist)
let views = ["albumCover": self.albumCover, "title": self.title, "artist": self.artist];
var constraints = Array<String>()
constraints.append("H:|-0-[albumCover]-0-|")
constraints.append("H:|-0-[title]-0-|")
constraints.append("H:|-0-[artist]-0-|")
constraints.append("V:|-0-[albumCover]-[title]-[artist]-0-|")
let aspectRatioConstraint = NSLayoutConstraint(item: self.albumCover, attribute: .width, relatedBy: .equal, toItem: self.albumCover, attribute: .height, multiplier: 1.0, constant: 0.0)
self.addConstraint(aspectRatioConstraint)
for constraint in constraints {
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: constraint, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
}
for view in self.subviews {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
}
class AlbumCell : UITableViewCell {
var firstAlbumView: AlbumView!
var secondAlbumView: AlbumView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.initControls()
self.setTheme()
self.doLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initControls() {
self.firstAlbumView = AlbumView(frame: CGRect.zero)
self.secondAlbumView = AlbumView(frame: CGRect.zero)
}
func setTheme() {
}
func doLayout() {
self.contentView.addSubview(self.firstAlbumView)
self.contentView.addSubview(self.secondAlbumView)
let views: [String: AnyObject] = ["firstAlbumView": self.firstAlbumView, "secondAlbumView": self.secondAlbumView];
var constraints = Array<String>()
constraints.append("H:|-15-[firstAlbumView(==secondAlbumView)]-15-[secondAlbumView(==firstAlbumView)]-15-|")
constraints.append("V:|-15-[firstAlbumView]-15-|")
constraints.append("V:|-15-[secondAlbumView]-15-|")
for constraint in constraints {
self.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: constraint, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
}
for view in self.contentView.subviews {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
}
//Details..
class DetailSongViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue()
}
/*override func previewActionItems() -> [UIPreviewActionItem] {
let regularAction = UIPreviewAction(title: "Regular", style: .default) { (action: UIPreviewAction, vc: UIViewController) -> Void in
}
let destructiveAction = UIPreviewAction(title: "Destructive", style: .destructive) { (action: UIPreviewAction, vc: UIViewController) -> Void in
}
let actionGroup = UIPreviewActionGroup(title: "Group...", style: .default, actions: [regularAction, destructiveAction])
return [actionGroup]
}*/
}
//Implementation..
extension LibraryViewController : UIViewControllerPreviewingDelegate {
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = self.tableView.indexPathForRow(at: location) else {
return nil
}
guard let cell = self.tableView.cellForRow(at: indexPath) else {
return nil
}
previewingContext.previewingGestureRecognizerForFailureRelationship.addObserver(self, forKeyPath: "state", options: .new, context: nil)
let detailViewController = DetailSongViewController()
detailViewController.preferredContentSize = CGSize(width: 0.0, height: 300.0)
previewingContext.sourceRect = cell.frame
return detailViewController
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
//self.show(viewControllerToCommit, sender: self)
}
override func observeValue(forKeyPath keyPath: String?, of object: AnyObject?, change: [NSKeyValueChangeKey : AnyObject]?, context: UnsafeMutablePointer<Void>?) {
if let object = object {
if keyPath == "state" {
let newValue = change![NSKeyValueChangeKey.newKey]!.integerValue
let state = UIGestureRecognizerState(rawValue: newValue!)!
switch state {
case .began, .changed:
self.navigationItem.title = "Peeking"
(object as! UIGestureRecognizer).isEnabled = false
case .ended, .failed, .cancelled:
self.navigationItem.title = "Not committed"
object.removeObserver(self, forKeyPath: "state")
DispatchQueue.main.async(execute: {
(object as! UIGestureRecognizer).isEnabled = true
})
case .possible:
break
}
}
}
}
}
class LibraryViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.initControls()
self.setTheme()
self.registerClasses()
self.registerPeekPopPreviews();
self.doLayout()
}
func initControls() {
self.tableView = UITableView(frame: CGRect.zero, style: .grouped)
}
func setTheme() {
self.edgesForExtendedLayout = UIRectEdge()
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
func registerClasses() {
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Default")
self.tableView.register(AlbumCell.self, forCellReuseIdentifier: "AlbumCell")
}
func registerPeekPopPreviews() {
//if (self.traitCollection.forceTouchCapability == .available) {
self.registerForPreviewing(with: self, sourceView: self.tableView)
//}
}
func doLayout() {
self.view.addSubview(self.tableView)
let views: [String: AnyObject] = ["tableView": self.tableView];
var constraints = Array<String>()
constraints.append("H:|-0-[tableView]-0-|")
constraints.append("V:|-0-[tableView]-0-|")
for constraint in constraints {
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: constraint, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
}
for view in self.view.subviews {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 5 : 10
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return (indexPath as NSIndexPath).section == 0 ? 44.0 : 235.0
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return section == 0 ? 75.0 : 50.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.0001
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "Library" : "Recently Added"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).section == 0 { //Library
let cell = tableView.dequeueReusableCell(withIdentifier: "Default", for: indexPath)
switch (indexPath as NSIndexPath).row {
case 0:
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = "Playlists"
case 1:
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = "Artists"
case 2:
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = "Albums"
case 3:
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = "Songs"
case 4:
cell.accessoryType = .disclosureIndicator
cell.textLabel?.text = "Downloads"
default:
break
}
}
if (indexPath as NSIndexPath).section == 1 { //Recently Added
let cell = tableView.dequeueReusableCell(withIdentifier: "AlbumCell", for: indexPath)
cell.selectionStyle = .none
return cell
}
return tableView.dequeueReusableCell(withIdentifier: "Default", for: indexPath)
}
}
Credo che dove mi sono perso è che non devo scorrere verso l'alto. So che quando tocco 3D un thread di testo e scorro verso l'alto ottengo le opzioni di risposta. Ma quando tocco 3D un album, si anima solo lo schermo mostrato sopra. Niente swiping o niente –
Questa operazione potrebbe essere eseguita utilizzando UIPreviewInteraction API.
https://developer.apple.com/documentation/uikit/uipreviewinteraction
è quasi simile al Peek e Pop API.
Qui abbiamo 2 fasi: Anteprima e Conferma che corrispondono a Peek e Pop nell'API successiva. abbiamo UIPreviewInteractionDelegate che ci dà l'accesso alla transizione attraverso queste fasi.
Allora, cosa si dovrebbe fare è, per replicare quanto sopra Apple Music Popup,
mostrano manuale di una sovrapposizione di sfocatura durante didUpdatePreviewTransition
Costruire un XI ter del menu in alto e mostrarlo durante didUpdateCommitTransition
- È possibile rendere la vista rimanere lì in fase di commitTransition fine.
In realtà, Apple ha creato una demo di questo sotto forma di app di chat.
Scaricare il codice di esempio da here e provarlo.
Sei sicuro che sia un 3D Touch e non solo una lunga pressione? Funziona anche con il mio iPhone 6+ che non ha 3D Touch. – Fogmeister
@Fogmeister Ok, quindi sono andato e ho disattivato il tocco 3D. Se noti che mostra le stesse cose ma in fondo con un pulsante Annulla aggiunto. Mi piacerebbe davvero fare entrambe le cose. Ma il problema di come lo farei è ancora valido. –
piuttosto che usare un peek e un pop segue non è possibile usare una sorta di gesto di tocco di forza per attivarlo? Al momento non sono sul mio computer, ma è quello che cerco. – Fogmeister