Så här uppdaterar du din app för iOS 11 Dra och släpp

Vad du ska skapa

iOS 11 har förhöjt iOS, i synnerhet för iPad, till en sann plattform för flera uppdrag, tack vare Drag och släpp. Detta lovar att oskärpa gränserna mellan appar, så att innehållet enkelt delas. Med iOS 11 kan innehållet flyttas på ett naturligt och intuitivt sätt, vilket ger Apples mobila enheter närmare paritet med den rikedom som skrivbords- och bärare använder.


Den här efterlängtade funktionen låter dig dra objekt till en annan plats i samma applikation eller till en annan applikation. Detta fungerar antingen via ett split-screen-arrangemang eller via dockan, med en kontinuerlig gest. Dessutom är användarna inte begränsade till att bara välja enskilda objekt, men kan dra flera objekt samtidigt. Många appar, inklusive systemprogram som foton och filer, utnyttjar flera val och dra flera filer.

Målen för denna handledning

Denna handledning kommer att introducera dig att dra och släppa, och dyka sedan djupare in i arkitekturen och strategin för att använda den nya Dra och släpp SDK-enheten i en tabellvisad app. Jag vill hjälpa utvecklare som dig själv att anpassa dina appar till det nya användargränssnittet som kommer att bli standard i framtida iOS-appar. 

I denna handledning kommer vi att täcka följande:

  • Förstå Drag och släpp
  • Implementera dra och släpp i en tabellvy 
  • Dra och släpp bästa praxis

I den andra halvan av denna handledning kommer vi att gå igenom de praktiska stegen för att möjliggöra en enkel tabellvy-app för att dra nytta av dra och släpp, med början av en av Apples standardmallar för tabellvy som är tillgängliga när du skapar ett nytt projekt i Xcode 9. Fortsätt och klon i handledningens GitHub repo om du vill följa med.

Förutsatt kunskap

Denna handledning förutsätter att du har erfarenhet som en iOS-utvecklare och har använt UIKit-bibliotek i Swift eller Objective-C, inklusive UITableView, och att du har någon förtrogenhet med delegater och protokoll.

Förstå Drag och släpp

Med Apples nomenklatur dras ett visuellt objekt från källplatsen och släpptes på destinationsplatsen. Detta kallas en dragaktivitet, där aktiviteten antingen äger rum inom en enda app (iPad och iPhone stöds) eller tvärs över två appar (endast tillgängliga på iPad). 

Medan en dragsession pågår, är både käll- och destinationsapparna fortfarande aktiva och kör som vanligt, och stöder användarinteraktioner. Faktum är att iOS, till skillnad från macOS, stöder flera samtidiga dragaktiviteter genom att använda flera fingrar. 

Men låt oss fokusera på ett enda dragobjekt, och hur det använder ett löfte som ett kontrakt för dess datapresentationer. 

Dra objekt som löften

Varje dragningsobjekt kan anses som ett löfte, en inneboende datarepresentation som kommer att släpas och släppas från en källa till dess destination. Drag-objektet använder en objektleverantör, som fyller dess registeredTypeIdentifiers med enhetliga typidentifierare, vilka är individuella datarepresentationer som den kommer att begå att leverera till dess avsedda destination tillsammans med en förhandsgranskningsbild (som är fastsatt under användarens beröringspunkt visuellt), som illustreras nedan:


Dragelementet är konstruerat genom UIDragInteractionDelegate från källplatsen och hanteras på destinationsplatsen via UIDropInteractionDelegate. Källplatsen måste överensstämma med NSItemProviderWriting protokoll och destinationsplatsen måste överensstämma med NSItemProviderReading protokoll. 

Det är en grundläggande översikt över att nominera en vy som ett dragobjekt, genom löften. Låt oss se hur vi implementerar en dragkälla från en vy innan du etablerar droppdestinationen.

Implementera en dragkälla

Fokusera vår uppmärksamhet på den första delen av dra och släpp-drakällan - vi måste utföra följande steg när användaren initierar en draaktivitet:

  1. Överensstämmer med utsikten till UIDragInterationDelegate protokoll.
  2. Förklara de dataobjekt som kommer att utgöra artikellöfte, via dragInteraction (_: itemsForBeginning :)
  3. Flytta dragsessionen med dragen i förväg för att användaren ska dra objekten till droppdestinationen.

Det första du behöver göra är att anpassa din nominerade vy till UIDragInterationDelegate protokoll, genom att skapa en ny UIDragInteraction instans och associera den med din ViewControllers åsikt är addInteraction fastighet samt dess delegat, enligt följande: 

 låt dragInteraction = UIDragInteraction (delegate: dragInteractionDelegate) view.addInteraction (dragInteraction)

Efter att du har förklarat din dragkälla fortsätter du att skapa ett dragobjekt, i huvudsak ett löfte om datarrepresentation, genom att implementera delegatmetoden dragInteraction (_: itemsForBeginning :), som systemet samtalar för att returnera en grupp av ett eller flera dragobjekt för att fylla i egenskapen för dragsessionens objekt. Följande exempel skapar en NSItemProvider från ett bildlöfte innan du returnerar en rad dataobjekt:

funk dragInteraction (_ interaktion: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] guard let imagePromise = imageView.image else return [] // Genom att returnera en tom array du inaktiverar att dra.  let provider = NSItemProvider (objekt: imagePromise) låt objekt = UIDragItem (itemProvider: provider) returnera [item]

Delegemetoden ovan svarar på en dragförfrågan som utlöses när användaren börjar dra objektet, med Gesture Recognizer (UIGestureRecognizer) skickar ett "drastart" -meddelande tillbaka till systemet. Detta är vad som väsentligen initierar "drag-sessionen". 

Därefter fortsätter vi med att implementera droppdestinationen, för att hantera det antal dragdrag som initierats i sessionen.

Implementera en droppdestination

På samma sätt, för att anpassa din nominerade vy för att acceptera och konsumera data som en del av droppdestinationen, måste du slutföra följande steg:

  1. Instantiate a DropInteraction.
  2. Förklara dataposttyperna (om några) du accepterar, med hjälp av dropInteraction (_: canHandle :).
  3. Implementera ett droppförslag med hjälp av dropInteraction (_: sessionDidUpdate :) protokollmetod som anger om du kommer att kopiera, flytta, vägra eller avbryta sessionen.
  4. Slutligen konsumera dataobjekten med hjälp av dropInteraction (_: performDrop :) protokollmetod.

Precis som vi konfigurerade vår syn för att aktivera dra, konfigurerar vi symmetriskt vår nominerade vy för att acceptera tappade objekt från en dracksession med hjälp av UIDropinteractionDelegate och genomföra dess DropInteraction delegeringsmetod:

låt dropInteraction = UIDropInteraction (delegat: dropInteractionDelegate) view.addInteraction (dropInteraction)

För att ange huruvida en vy kan acceptera dragen eller vägrar implementerar vi dropInteraction (_: canHandle :) protokollmetod. Följande metod låter vår åsikt berätta för systemet om det kan acceptera objekten, genom att ange vilken typ av objekt det kan ta emot - i detta fall UIImages.

func dropInteraction (_ interaktion: UIDropInteraction, canHandle session: UIDropSession) -> Bool // Ange det acceptabla droppobjektet här här returnera session.canLoadObjects (ofClass: UIImage.self)

Om visningsobjektet inte accepterar några droppinteraktioner, ska du returnera falskt från den här metoden. 

Därefter bind ett förslag till droppe för att acceptera data från droppsessionen. Även om det här är en valfri metod, rekommenderas det starkt att du implementerar den här metoden, eftersom det ger visuella indikeringar om huruvida droppen kommer att leda till att objektet kopieras, flyttas eller om droppen helt kommer att vägras. Genom att genomföra dropInteraction (_: sessionDidUpdate :) protokollmetoden, som returnerar a UIDropProposal, du anger förslagstyp med hjälp av den specifika operationsräkningstypen (UIDropOperation). De giltiga typerna du kan returnera är:

  • annullera
  • förbjuden
  • kopia
  • flytta
funktionsdropinteraktion (_ interaktion: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal // Signal till systemet som du flyttar objektet från källprogrammet (du kan också ange .kopiera att kopiera i motsats till att flytta) returnera UIDropProposal ( operation: .move)

Och slutligen, för att konsumera innehållet i dataobjektet i din destinationsadress, implementerar du dropInteraction (_: performDrop :) protokollmetoden i bakgrundskön (i stället för i huvudkön-detta säkerställer respons). Detta illustreras nedan: 

func dropInteraction (_ interaktion: UIDropInteraction, performDrop session: UIDropSession) // Använda UIImage dra objekt session.loadObjects (ofClass: UIImage.self) objekt i låt bilder = objekt som! [UIImage] self.imageView.image = images.first

Vi har visat hur du skulle implementera dra och släppa i en anpassad vy, så nu går vi vidare till den praktiska delen av denna handledning och implementerar dra och släpp på en app med en tabellvy.

Implementera dra och släpp i en tabellvy

Hittills har vi diskuterat hur man implementerar dra och släpp i anpassade vyer, men Apple har också gjort det enkelt att öka tabellvyer och samlingsvyer med dra och släpp. Medan textfält och vyer automatiskt stöder dra och släpp ut ur rutan, visar tabell- och samlingsvyer specifika metoder, delegater och egenskaper för anpassning av drag och släpp beteenden. Vi tar en titt på det här inom kort.

Börja med att skapa ett nytt projekt i Xcode 9, så att du väljer Master-detaljapp från mallfönstret:

Innan du börjar arbeta med resten av stegen, fortsätt och bygg och kör projektet och leka med det lite. Du får se att det genererar ett nytt tidstämplingsdatum när du väljer pluset (+) knapp. Vi kommer att förbättra den här appen genom att tillåta användaren att dra och beställa tidstämplarna. 

Dra och släpp stöds i tabellvyer (såväl som samlingar) genom specialiserade API-skivor som gör det möjligt att dra och släppa rader, genom att överensstämma med våra tabellvyer för att anta både UITableViewDragDelegate och UITableViewDropDelegate protokoll. Öppna upp MasterViewController.swift fil och lägg till följande till viewDidLoad () metod:

åsidosätta func viewDidLoad () super.viewDidLoad () ... self.tableView.dragDelegate = self self.tableView.dropDelegate = self ... 

Som vi gjorde med anpassade vyer måste vi hantera den nya drassessionen när användaren drar en vald rad eller flera rader / val. Vi gör detta med delegatmetoden Tableview (_: itemsForBeginning: vid :). Inom den här metoden returnerar du antingen en populär grupp som börjar dra de valda raderna eller en tom array för att hindra användaren från att dra innehåll från den specifika indexvägen. 

Lägg till följande metod för din MasterViewController.swift fil:

func tableView (_ tableView: UITableView, itemsForBeginning session: UIDragSession, på indexPath: IndexPath) -> [UIDragItem] let dateItem = self.objects [indexPath.row] as! String låt data = dateItem.data (using: .utf8) låt itemProvider = NSItemProvider () itemProvider.registerDataRepresentation (forTypeIdentifier: kUTTypePlainText som sträng, synlighet: .all) slutförd i slutförandet (data, noll) returnera nil returnera [UIDragItem itemProvider: itemProvider)]

Några av de tillagda koden borde redan vara kända för dig från föregående avsnitt, men i huvudsak är det vi gör är att skapa ett dataobjekt från det valda objektet, linda det i en NSItemProvider, och returnera den i en DragItem

Om vi ​​uppmärksammar droppsessionen, fortsätt med att lägga till följande två metoder:

func tableView (_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal if tableView.hasActiveDrag om session.items.count> 1 returnera UITableViewDropProposal (operation: .cancel) annars returnera UITableViewDropProposal (operation: .move, intention: .insertAtDestinationIndexPath) annars returnera UITableViewDropProposal (operation:. kopiera, avsikt: .insertAtDestinationIndexPath)

Den första metoden berättar för systemet att det kan hantera strängdatatyper som en del av sin droppsession. Den andra delegatmetoden, Tableview (_: dropSessionDidUpdate: withDestinationIndexPath :), spårar den potentiella droppplatsen och meddelar metoden med varje ändring. Den visar också visuell återkoppling för att låta användaren veta om en specifik plats är förbjuden eller acceptabel, med hjälp av en liten visuell ikonkod. 

Slutligen hanterar vi droppen och konsumerar dataposten, ringer Tableview (_: performDropWith :), hämtar den raderade dataobjektraden, uppdaterar tabellvyns datakälla och sätter in nödvändiga rader i tabellen.

func tableView (_ tableView: UITableView, performDropWith koordinator: UITableViewDropCoordinator) låt destinationIndexPath: IndexPath om låt indexPath = coordinator.destinationIndexPath destinationIndexPath = indexPath else // Hämta senaste indexvägen för tabellvyn. let section = tableView.numberOfSections - 1 let row = tableView.numberOfRows (inSection: section) destinationIndexPath = IndexPath (rad: rad, avsnitt: avsnitt) coordinator.session.loadObjects (ofClass: NSString.self) poster i // Konsumera dra objekt. låt stringItems = föremål som! [String] var indexPaths = [IndexPath] () för (index, item) i stringItems.enumerated () let indexPath = IndexPath (rad: destinationIndexPath.row + index, avsnitt: destinationIndexPath.section) self.objects.insert , på: indexPath.row) indexPaths.append (indexPath) tableView.insertRows (vid: indexPaths, med: .automatic)

Vidare läsning

Mer information om hur du stöder dra och släpp i dina tabellvyer finns i Apples egenutvecklade dokumentation om stöd för dra och släpp i tabellvyer.

Dra och släpp bästa praxis

Innehållet vi täckte ska hjälpa dig att implementera dra och släppa i dina appar, så att användarna kan flytta visuellt och interaktivt innehåll inom sina befintliga appar, samt över appar. 

Tillsammans med den tekniska kunskapen om hur man implementerar dra och släpp, är det dock nödvändigt att du tar dig tid att överväga hur du skulle implementera dra och släppa, följ de bästa praxis som Apple föreslagit i deras Human Interface Guidelines (HIG), i för att ge användarna bästa möjliga användarupplevelse iOS 11. 

För att paketera upp, kommer vi att röra på några av de viktigaste aspekterna att överväga, med början av visuella signaler. Enligt HIG är den grundläggande upplevelsen med drag och släpp att när en användare interagerar med något innehåll, visar visuella signaler till användaren en aktiv dragsession, betecknad med att innehållselementet stiger tillsammans med ett märke som anger när det släpper är eller är inte möjligt.

Vi har redan använt denna bästa praxis i våra tidigare exempel när vi inkluderade Tableview (_: dropSessionDidUpdate: withDestinationIndexPath :) metod som anger om droppdestinationen är ett drag, kopiera eller förbjudet. Du bör säkerställa med anpassade visningar och interaktioner att du behåller den förväntade uppsättningen beteenden som andra iOS 11-appar, särskilt systemprogram, stöder. 

En annan viktig aspekt att tänka på är att avgöra om din dragsession kommer att resultera i ett drag eller en kopia. Som en allmän tumregel föreslår Apple att när du arbetar inom samma app, bör det generellt leda till ett drag, medan det är mer meningsfullt att kopiera dataobjektet när du drar mellan olika appar. Även om det finns undantag, är den underliggande principen självklart att det ska vara meningsfullt för användaren, och vad de förväntar sig ska ske. 

Du bör också tänka på källor och destinationer, och om det är vettigt att dra något eller inte. 

Låt oss ta en titt på några av Apples egna systemverktyg. I anteckningar kan du till exempel välja och dra textinnehåll till andra platser i appen eller över till andra appar på iPad via split-skärmen. I appen Påminnelser kan du flytta påminnelseposter från en lista till en annan lista. Tänk vad gäller funktionalitet när du bestämmer hur användarna använder ditt innehåll. 

Apples vägledning är att allt innehåll som kan redigeras bör stödja att acceptera droppat innehåll och allt innehåll som kan väljas bör acceptera dragbart innehåll, förutom att stödja kopia och klistra för dessa typer av element. Genom att utnyttja standard systemtextvyer och textfält får du stöd för att dra och släppa ut ur lådan. 

Du bör också stödja flera objektdrag och släpp, i motsats till att endast stödja enskilda objekt, där användare kan använda mer än ett finger för att välja flera objekt samtidigt, stapla de valda objekten i en grupp som ska tappas till sina avsedda destinationer. Ett exempel på detta är att välja flera bilder i appen Foton eller flera filer i appen Filer. 

En slutgiltig riktlinje är att ge användarna möjlighet att vända om en åtgärd, eller "ångra" en droppe. Användare har länge varit vana vid konceptet att ångra en åtgärd i de flesta populära applikationerna, och dra och släpp ska inte vara något undantag. Användare bör ha förtroendet för att kunna initiera en dra och släppa och kunna vända den åtgärden om de släpper elementet i fel destination. 

Vidare läsning

Det finns många fler dra och släppa riktlinjer för bästa praxis utöver vad vi har tittat på, bland annat hur man stöder visade indikatorindikatorer, visade misslyckade borttagna åtgärder och framåtriktningsindikatorer för icke-omedelbara drag-sessioner, till exempel dataöverföringar. Läs Apples riktlinjer för iOS-gränssnitt om dra och släpp, för en fullständig lista över bästa praxis. 

Slutsats

I den här handledningen har du lärt dig hur du berikar dina iOS-appar med dra och släpp, med tanke på iOS 11. Under tiden utforskade vi hur du aktiverar både anpassade visningar och tabellvisningar som dragkällor och tappar destinationer. 

Som en del av IOS-utvecklingen mot ett mer gestidigt användargränssnitt, kommer drag och släpp utan tvekan att bli en förväntad funktion för användare över hela systemet, och som sådan bör alla tredjepartsapplikationer också överensstämma. Och lika viktigt som att dra och släppa, måste du implementera det rätt, så att det blir andra natur för användarna, som omfattar enkelhet och funktionalitet.

Och medan du är här, kolla in några av våra andra inlägg i IOS App Development!