
O mecanismo de 
Drag & Drop em execução no 
iOS 11 e 
iOS 12 é uma maneira de copiar ou mover graficamente de forma assíncrona ou de dados, em um único aplicativo e entre diferentes aplicativos. Embora essa tecnologia tenha cerca de 30 anos, ela literalmente se tornou uma tecnologia "inovadora" no 
iOS devido ao fato de que, ao arrastar algo no 
iOS , o 
multitouch permite interagir livremente com o resto do sistema e coletar dados para redefinir a partir de diferentes aplicativos.
iOS torna possível capturar vários elementos ao mesmo tempo. Além disso, eles não precisam ter uma acessibilidade conveniente para a seleção: você pode pegar o primeiro objeto, ir para outro aplicativo e pegar outra coisa - todos os objetos serão coletados em uma “pilha” embaixo do seu dedo. Em seguida, chame o dock universal na tela, abra qualquer aplicativo e capture o terceiro objeto e vá para a tela com os aplicativos em execução e, sem liberar os objetos, despeje-os em um dos programas abertos. Essa liberdade de ação é possível no 
iPad , no 
iPhone , a cobertura de 
Drag & Drop no 
iOS limitada à estrutura de um aplicativo.
Os aplicativos mais populares ( 
Safary , 
Chrome , 
IbisPaint X , 
Mail , 
Photos , 
Files etc.) já possuem um mecanismo de 
Drag & Drop . Além disso, a 
Apple forneceu aos desenvolvedores uma 
API muito simples e intuitiva para incorporar o mecanismo 
Drag & Drop no seu aplicativo. O mecanismo 
Drag & Drop , assim como os gestos, funciona no 
UIView e usa o conceito de 
Interações , um pouco como gestos, para que você possa pensar no mecanismo 
Drag & Drop simplesmente como um gesto realmente poderoso.
Além de gestos, é muito fácil integrar seu aplicativo. Especialmente se seu aplicativo usa a tabela 
UITableView ou a coleção 
UICollectionView , porque para eles 
API Drag & Drop aprimorada e aumentada para um nível mais alto de abstração, no sentido de que a 
Collection View si ajuda você com o 
indexPath do elemento de coleção que você deseja arrastar e soltar 
Drag . Ela sabe onde está o seu dedo e o interpreta como o 
indexPath do elemento de coleção que você está “arrastando” 
Drag no momento, ou como o 
indexPath do elemento de coleção em que você está “soltando” 
Drop algo. Portanto, a 
Collection View fornece o 
indexPath , mas, de outra forma, é absolutamente a mesma 
API Drag & Drop que para um 
UIView regular.
O processo de 
Drag & Drop no 
iOS possui 4 fases diferentes:
Lift
Levantar (levantar) - é quando o usuário executa um 
longo gesto de 
pressão , indicando o elemento que será "arrastar e soltar". Nesse momento, uma chamada " 
lift preview " muito leve do elemento indicado é formada e o usuário começa a arrastar os dedos.

Arrastar (arrastar e soltar)
Arrastar (arrastar e soltar) - é quando o usuário move o objeto na superfície da tela. Durante esta fase, a " 
lift preview " para este objeto pode ser modificada (um sinal de mais verde "+" ou outro sinal aparece) ...

... alguma interação com o sistema também é permitida: você pode clicar em outro objeto e adicioná-lo à sessão atual de "arrastar e soltar":

Drop
Queda ocorre quando o usuário levanta um dedo. Nesse momento, duas coisas podem acontecer: o objeto 
Drag será destruído ou o objeto 
Drop será "descartado" no destino.

Transferência de dados
Se o processo de 
arrastar e soltar não foi cancelado e a redefinição " 
Soltar " ocorreu, ocorre a 
Transferência de dados (transferência de dados), na qual o "ponto de soltar" solicita dados da "origem" e ocorre a transferência assíncrona de dados.
Neste tutorial, mostraremos como 
integrar facilmente 
o mecanismo 
Drag & Drop em seu aplicativo 
iOS usando o aplicativo de demonstração da Image Gallery, tirado 
do curso de dever de casa de Stanford, CS193P .
Dotaremos a 
Collection View capacidade de nos preenchermos com imagens FORA, além de reorganizarmos elementos INSIDE usando o mecanismo 
Drag & Drop . Além disso, esse mecanismo será usado para despejar elementos desnecessários da 
Collection View da 
Collection View em uma "lixeira", que é uma 
UIView normal e é representada por um botão no painel de navegação. Também podemos compartilhar imagens coletadas em nossa Galeria com outros aplicativos usando o mecanismo 
Drag & Drop , por exemplo, 
Notes ou 
Notes ou 
Mail ou uma biblioteca de 
Photo ( 
Photo ).
Mas antes de focar na implementação do mecanismo 
Drag & Drop no aplicativo de demonstração “Image Gallery”, analisarei seus principais componentes muito brevemente.
Recursos do aplicativo de demonstração "Galeria de imagens"
A interface do usuário ( 
UI ) do aplicativo Image Gallery é muito simples. Este é um "trecho de tela" do 
Image Gallery Collection View Controller inserido no 
Navigation Controller :

A parte central do aplicativo é certamente o 
Image Gallery Collection View Controller , que é suportado pela classe 
ImageGalleryCollectionViewController com o Image Gallery Model como uma variável 
var imageGallery = ImageGallery () :

O modelo é representado por uma 
estrutura struct ImageGallery contendo uma matriz de imagens, na qual cada imagem é descrita por uma 
estrutura struct ImageModel contendo o 
URL localização 
URL imagem (não vamos armazenar a imagem em si) e sua proporção:

Nosso 
ImageGalleryCollectionViewController implementa o protocolo 
DataSource :

A célula personalizada na coleção de células contém uma imagem 
imageView: UIImageView! e indicador de atividade 
giratório: UIActivityIndicatorView! e é suportado pela 
subclass personalizada 
ImageCollectionViewCell da classe 
UICollectionViewCell :

Public API da classe 
ImageCollectionViewCell é o 
URL da imagem 
imageURL . Assim que a instalamos, nossa 
UI atualizada, ou seja, os dados da imagem são selecionados de forma assíncrona neste 
imageURL e exibidos na célula. Enquanto os dados estão sendo recuperados da rede, o indicador de atividade do 
girador está funcionando, indicando que estamos no processo de recuperação de dados.
Uso a fila global 
(qos: .userInitiated) com o argumento de qualidade de serviço 
qos para obter dados em um determinado 
URL , definido como 
.userInitiated porque seleciono os dados a pedido do usuário:

Cada vez que você usa suas próprias variáveis dentro de um fechamento, no nosso caso são 
imageView e 
imageURL , o compilador obriga você a se colocar na frente delas 
. para que você se pergunte: "Existe um" link cíclico da memória "?" Não temos um " 
memory cycle " explícito aqui, porque 
o próprio 
eu não tem um ponteiro para esse fechamento.
No entanto, no caso de multithreading, lembre-se de que as 
células na 
Collection View são reutilizáveis graças ao método 
dequeueReusableCell . Cada vez que uma célula (nova ou reutilizada) aparece na tela, a imagem é baixada da rede de forma assíncrona (nesse momento, o “ 
botão giratório ” do indicador de atividade do 
botão giratório está girando).
Assim que o download é concluído e a imagem é recebida, a 
UI desta célula de coleção é atualizada. Mas não esperamos o carregamento da imagem, continuamos a rolar pela coleção e a célula da coleção que marcamos sai da tela sem atualizar nossa 
UI . No entanto, uma nova imagem deve aparecer abaixo e a mesma célula que saiu da tela será reutilizada, mas para outra imagem, que pode carregar e atualizar rapidamente a 
UI do 
UI . Nesse momento, o carregamento da imagem iniciado anteriormente nesta célula retornará e a tela será atualizada, o que levará a um resultado incorreto. Isso ocorre porque executamos coisas diferentes que funcionam com a rede em segmentos diferentes. Eles voltam em momentos diferentes.
Como podemos resolver a situação?
Dentro da estrutura do mecanismo 
GCD que usamos, não podemos cancelar o download da imagem da célula que saiu da tela, mas podemos, quando nossos dados 
imageData chegam da rede, verificar o 
URL URL que causou o carregamento desses dados e compará-lo com o que o usuário deseja ter. nesta célula no momento, ou seja, 
imageURL . Se eles não corresponderem, não atualizaremos a célula da 
UI do 
UI e aguardaremos os dados de imagem necessários:

Essa linha de código aparentemente absurda 
url == self.imageURL faz com que tudo funcione corretamente em um ambiente multithread que requer uma imaginação não padrão. O fato é que algumas coisas na programação multithread acontecem em uma ordem diferente da escrita do código.
Se não foi possível selecionar os dados da imagem, uma imagem é gerada com uma mensagem de erro na forma da sequência “Erro” e um emoji com “carranca”. Apenas o espaço vazio em nossa 
Collection View pode confundir um pouco o usuário:

Não queremos que a imagem com a mensagem de erro repita o 
aspecto da relação dessa imagem de erro, pois, neste caso, o texto junto com o emoji será esticado ou compactado. Gostaríamos que fosse neutro - quadrado, ou seja, teria uma relação de aspecto próxima a 1,0.

Devemos informar nosso 
Controller sobre esse desejo, para que ele corrija a proporção 
aspectRatio para o 
indexPath correspondente em seu 
imageGallery Model. Essa é uma tarefa interessante, existem várias maneiras de resolvê-la e escolheremos a mais fácil delas - usando o Fechamento 
opcional var closAspectRatio: (() -> Void)? . Pode ser igual a 
zero e não precisa ser instalado se isso não for necessário:

Ao chamar o fechamento 
changeAspectRatio? () No caso de busca incorreta de dados, eu uso a cadeia 
Opcional . Agora, qualquer pessoa interessada em algum tipo de configuração ao receber uma imagem incorreta pode definir esse fechamento para algo específico. E é exatamente isso que fazemos em nosso 
Controller no método 
cellForItemAt :

Detalhes podem ser encontrados 
aqui .
Para exibir imagens com o 
aspectRatio correto, o método 
sizeForItemAt do delegado 
UICollectionViewDelegateFlowLayout é 
usado :

Além da 
Collection View imagens do modo de 
Collection View , em nossa 
UI colocamos um 
Bar Button no painel de navegação com uma imagem personalizada do 
GarbageView contendo uma "lixeira" como uma 
sub-visualização :

Nesta figura, as cores de plano de fundo do 
GarbageView em si e o botão 
UIButton com a imagem da "lata de lixo" (na verdade, existe um plano de fundo transparente) são alteradas especialmente para que você veja que o usuário que "despeja" as imagens da Galeria na "lata de lixo" muito mais espaço de manobra ao "soltar" 
Solte do que apenas o ícone da lixeira.
A classe 
GarbageView possui dois inicializadores e ambos usam o método 
setup () :

No método 
setup () , também adiciono 
myButton como uma 
subvisão com a imagem da “lata de lixo” extraída do 
Bar Button padrão do 
Bar Button :
 Defino
Defino um plano de fundo transparente para o 
GarbageView :

O tamanho da lata de lixo e sua posição serão determinados no método 
layoutSubviews () da classe 
UIView , dependendo dos 
limites do UIView fornecido:

Esta é a versão inicial do aplicativo de demonstração "Image Gallery", está localizada no 
Github na pasta 
ImageGallery_beginning . Se você executar esta versão do aplicativo "Image Gallery", verá o resultado do aplicativo trabalhando nos dados de teste, que serão excluídos posteriormente e preencherão a "Image Gallery" exclusivamente FORA:

O plano para implementar o mecanismo 
Drag & Drop em nosso aplicativo é o seguinte:
- primeiro, dotaremos nossa Collection Viewcapacidade de "arrastar"Dragpartir dela UIImage imagens externamente e localmente,
- ensinaremos nossa coleção de imagens do modo de Collection ViewdeCollection Viewa aceitar oDrag"arrastado" externa ou localmente do UIImage ,
- também ensinaremos nosso GarbageView com o botão da lata de lixo a aceitar imagens UIImage arrastadas da Collection Viewlocal e removê-las daCollection View
Se você for ao final deste tutorial e concluir todas as alterações de código necessárias, receberá a versão final do aplicativo de demonstração “Image Gallery”, na qual o mecanismo 
Drag & Drop foi implementado. Está localizado no 
Github na pasta 
ImageGallery_finished .
O desempenho do mecanismo 
Drag & Drop na sua 
Collection View fornecido por dois novos delegados.
Os métodos 
do primeiro delegado, dragDelegate , são configurados para inicializar e personalizar os arrastar e soltar 
Drags .
Os métodos do segundo delegado, 
dropDelegate , concluem o arrastar e soltar do 
Drags e, basicamente, fornecem 
Data transfer dados e configurações personalizadas de animação quando o 
Drop redefinido, além de outras coisas semelhantes.
É importante observar que esses dois protocolos são completamente independentes. Você pode usar um ou outro protocolo se precisar apenas “arrastar” 
Drag ou soltar “ 
Drop ”, mas você pode usar os dois protocolos de uma só vez e simultaneamente 
Drag e soltar 
Drag e 
Drop , o que abre funcionalidades adicionais 
Drag & Drop mecanismo para reordenar itens na sua 
Collection View .
Arrastar e soltar elementos de Drag Collection View
A implementação do protocolo 
Drag é muito simples, e a primeira coisa que você deve sempre fazer é se definir como um delegado 
dragDelegate :

E, é claro, no topo da classe 
ImageGalleryCollectionViewController, você deve dizer "Sim", implementamos o protocolo 
UICollectionViewDragDelegate :

Assim que fazemos isso, o compilador começa a "reclamar", clicamos no círculo vermelho e nos perguntam: "Deseja adicionar os métodos necessários do protocolo 
UICollectionViewDragDelegate ?"
Eu respondo: "Claro que quero!" e clique no botão 
Fix :

O único método necessário do protocolo 
UICollectionViewDragDelegate é o método 
itemsForBeginning , que informará o sistema 
Drag que "arrastamos e 
soltamos ". O método 
itemsForBeginning é chamado quando o usuário começa a "arrastar" ( 
Dragging ) uma célula na 
célula de coleção.
Observe que, nesse método, a 
Collection View adicionou 
indexPath . Isso nos dirá qual elemento da coleção, qual 
indexPath , vamos “arrastar e soltar”. Isso é realmente muito conveniente para nós, pois é o aplicativo responsável pelo uso dos argumentos 
session e 
indexPath para 
descobrir como lidar com esse arrastar e soltar do 
Drag .
Se a matriz 
[UIDragItems] dos elementos " 
arrastáveis " for retornada, o "arrastar" do 
Drag inicializado; se a matriz vazia 
[] for retornada, o "drag" do 
Drag ignorado.
Vou criar uma pequena função 
dragItems privada (em: indexPath) com o argumento 
indexPath . Retorna a matriz 
[UIDragItem] que 
precisamos .

Como é um 
UIDragItem de arrastar e soltar?
Ele tem apenas uma coisa muito importante chamada 
itemProvider . 
itemProvider é apenas algo que pode fornecer dados que serão arrastados.
E você tem o direito de perguntar: "E o" arrastar e soltar "de um elemento 
UIDragItem que simplesmente não possui dados?" O item que você deseja arrastar pode não ter dados, por exemplo, porque a criação desses dados é uma operação cara. Pode ser uma imagem de 
imagem ou algo que exija o download de dados da Internet. O melhor é que a operação 
Drag & Drop é completamente assíncrona. Quando você começa a arrastar e soltar 
Drag , é realmente um objeto muito leve ( 
lift preview ), você o arrasta para qualquer lugar e nada acontece durante esse "arrastar". Mas assim que você "solta" 
Drop algum lugar seu objeto, sendo um 
itemProvider , ele realmente deve fornecer ao seu objeto "arrastado" e "jogado" dados reais, mesmo que demore um certo tempo.
Felizmente, existem muitos 
provedores de itens internos . Essas são classes que já existem no 
iOS e são 
itensPoviders , como, por exemplo, 
NSString , que permite arrastar e soltar texto sem fontes. Obviamente, esta é uma imagem 
UIImage . Você pode selecionar e arrastar e soltar imagens 
UIImages por toda parte . A classe 
NSURL , que é absolutamente maravilhosa. Você pode ir para a página da 
Web , selecionar o 
URL e soltá-lo onde quiser. Pode ser um link para um artigo ou um 
URL para uma imagem, como será em nossa demonstração. Essas são as classes de cores do 
elemento do mapa 
UIColor , 
MKMapItem , contato 
CNContact no catálogo de endereços, você pode selecionar e arrastar várias coisas. Todos eles são 
itemProviders .
Vamos “arrastar e soltar” a imagem 
UIImage . Ele está localizado na célula da célula 
Collection View com 
indexPath , que me ajuda a selecionar a célula da 
célula , obter o 
Outlet imageView e obter a imagem da 
imagem .
Vamos expressar essa idéia com algumas linhas de código.
Primeiro, solicito minha 
Collection View sobre uma 
célula para o elemento do 
item correspondente a este 
indexPath .

O 
método cellForItem (at: IndexPath) para o modo de 
Collection View funciona apenas para células visíveis, mas, é claro, funcionará no nosso caso, porque eu “arrasto e solto” o elemento de coleção de 
Drag na tela e fica visível.
Então, eu tenho uma célula de 
célula "arrastável".
Em seguida, eu uso o operador as 
? para esta célula para que ele tenha um TIPO da minha 
subclass personalizada. E se isso funcionar, recebo um 
Outlet imageView , do qual 
retiro sua 
imagem . Eu apenas “capturei” a imagem da 
imagem para este 
indexPath .
Agora que tenho uma imagem, basta criar um desses 
UIDragItems usando a imagem resultante como 
itemProvider , ou seja, o que nos fornece os dados.
Posso criar 
dragItem usando o construtor 
UIDragItem , que aceita 
itemProvider como argumento:

Em seguida, criamos um 
itemProvider para a imagem da 
imagem também usando o construtor 
NSItemProvider . Existem vários construtores para o 
NSItemProvider , mas entre eles existe um realmente maravilhoso - o 
NSItemProvider (objeto: NSItemProviderWriting) :

Você simplesmente fornece o objeto de 
objeto para esse construtor 
NSItemProvider e ele sabe como fazer com que 
itemProvider seja 
usado . Como um 
objeto, dou à imagem a 
imagem que recebi da célula da 
célula e obtenho o 
itemProvider para 
UIImage .
E isso é tudo. Criamos 
dragItem e devemos devolvê-lo como uma matriz com um elemento.
Mas antes de eu voltar dragItem , eu vou fazer mais uma coisa, ou seja, para definir a variável localObject para dragItem , igual à imagem resultante imagem .
O que isso significa?
Se você executar “arrastar e soltar” Draglocalmente, isto é, dentro do aplicativo, não precisará passar por todo esse código associado ao itemProvider , através da recuperação de dados assíncrona. Você não precisa fazer isso, basta pegar o localObject e usá-lo. Este é um tipo de "curto-circuito" com "arrastar e soltar" local Drag.O código que escrevemos funcionará ao "arrastar" Dragfora de nossa coleção Collection Viewpara outros aplicativos, mas se "arrastarmos" Draglocalmente, poderemos usar localObject . Em seguida, retorno uma matriz de um elemento dragItem .A propósito, se, por algum motivo, não consegui obter uma imagem para essa célula da célula , retornei uma matriz vazia [] , isso significa que o "arrastar e soltar" Dragfoi cancelado. Além de objeto local localObject , você pode se lembrar do contexto local localContext para a nossa
Além de objeto local localObject , você pode se lembrar do contexto local localContext para a nossa Dragsessão a sessão . No nosso caso, será uma coleção de CollectionView e é útil para nós depois: Ter "arrastar e soltar"
Ter "arrastar e soltar" Drag, você pode adicionar mais itens itens a esta "arrastar e soltar", apenas fazendo o gesto da torneira sobre eles. Como resultado, você pode arrastarDragmuitos itens de cada vez. E isso é fácil de implementar com outro método delegado , UICollectionViewDragDelegate , muito semelhante ao método itemsForeginning , um método chamado itemsForAddingTo . O método itemsForAddingTo parece exatamente igual ao item itemsForÂeginning e retorna exatamente a mesma coisa, porque também fornece um caminho de indexação do que o usuário “gravou” durante o processo de “arrastar Drage soltar” , e eu só preciso colocar a imagem da célula na que o usuário “gravou” e devolva-o. Retornar uma matriz vazia [] do método itemsForAddingToleva ao fato de que o gesto de toque será interpretado da maneira usual, ou seja, como a escolha dessa célula da célula .E é tudo o que precisamos para arrastar e soltar
Retornar uma matriz vazia [] do método itemsForAddingToleva ao fato de que o gesto de toque será interpretado da maneira usual, ou seja, como a escolha dessa célula da célula .E é tudo o que precisamos para arrastar e soltar Drag.Iniciamos o aplicativo.Eu seleciono a imagem "Veneza", seguro por um tempo e começo a mover ... ... e podemos realmente arrastar essa imagem para o aplicativo
... e podemos realmente arrastar essa imagem para o aplicativo Photos, como você vê o sinal de mais verde "+" no canto superior esquerdo da imagem "arrastável". Posso executar um gesto de toque em outra imagem Artika da coleção Collection View... ... e agora podemos inserir duas imagens no aplicativo
... e agora podemos inserir duas imagens no aplicativo Photos: Como o
Como o Photosmecanismo já está embutido no aplicativoDrag & Dropentão tudo funciona muito bem e é legal.Portanto, o "arrastar" Drage o "despejo" da Dropimagem da Galeria para outros aplicativos funcionam para mim . Não precisei fazer muito no meu aplicativo, exceto para entregar a imagem da imagem como uma matriz [UIDragItem] . Esse é um dos muitos recursos excelentes do mecanismo Drag & Drop- é muito fácil fazê-lo funcionar nas duas direções.Redefinir Dropimagens para a coleçãoCollection View
Agora precisamos fazer uma Dropparte da minha coleção Collection Viewpara que possamos "despejar" Dropqualquer imagem "arrastada" DENTRO desta coleção. Uma imagem "arrastável" pode "vir" de FORA, ou diretamente DENTRO desta coleção.Para fazer isso, nós fazemos a mesma coisa feita para delegar dragDelegate , ou seja, fazer a nós mesmos, o auto , delegar dropDelegate no método do viewDidLoad : Mais uma vez, temos que subir até o topo da nossa classe ImageGalleryCollectionViewController e verificar protocolo implementação UICollectionViewDropDelegate :
Mais uma vez, temos que subir até o topo da nossa classe ImageGalleryCollectionViewController e verificar protocolo implementação UICollectionViewDropDelegate : Assim que adicionamos nosso novo protocolo, o compilador novamente começou a "reclamar" de que não implementamos esse protocolo. Clicamos no botão
Assim que adicionamos nosso novo protocolo, o compilador novamente começou a "reclamar" de que não implementamos esse protocolo. Clicamos no botão Fixe os métodos necessários deste protocolo aparecem à nossa frente. Nesse caso, somos informados de que devemos implementar o método performDrop : devemos fazer isso, caso contrário, uma “redefinição” não ocorrerá
devemos fazer isso, caso contrário, uma “redefinição” não ocorrerá Drop. Na verdade, vou implementar o método performDrop por último, porque existem alguns outros Applemétodos altamente recomendados que você precisa implementar para a Droppeça. Isso é canHandle e dropSessionDidUpdate : Se implementarmos esses dois métodos, obteremos um pequeno sinal de adição verde “+” quando arrastarmos imagens de FORA para nossa coleção
Se implementarmos esses dois métodos, obteremos um pequeno sinal de adição verde “+” quando arrastarmos imagens de FORA para nossa coleção ollection Viewe, além disso, elas não tentarão despejar o que não entendemos.Vamos implementar o canHandle . sua versão do método canHandle , que se destina à coleta ollection View, mas esse método ollection Viewparece exatamente o mesmo que o método semelhante para UIView regular , não há indexPath , basta retornar o session.canLoadObjects (ofClass: UIImage.self) , e isso significa que eu aceito o "reset" dos objetos deste cl PAS em minha coleção ollection View: Mas isso não é suficiente para "despejar" a
Mas isso não é suficiente para "despejar" a Dropimagem na minha coleção Collection ViewFORA.Se o "reset" Dropa imagem ocorre dentro da coleção Collection View, o usuário vai reorganizar seus próprios elementos de itens através do mecanismo Drag & Drop, em seguida, apenas uma imagem de um UIImage e implementação do método canHandle será semelhante à maneira acima.Mas se o "dumping" da Dropimagem ocorrer FORA, então devemos manipular apenas o "arrastar e soltar" Dragque representa a imagem UIImage junto com URLesta imagem, pois não vamos armazenar as imagens UIImage diretamenteno modelo. Nesse caso, retornarei true somente no método canHandle se algumas condições forem atendidas ao mesmo tempo : session.canLoadObjects (ofClass: NSURL.self) && session.canLoadObjects (ofClass: UIImage.self) : preciso determinar se estou lidando com uma "redefinição" FORA OU DENTRO. Eu vou fazer isso com a ajuda de constantes calculadas isself , para o qual o cálculo que eu possa usar esse tipo de coisa na
preciso determinar se estou lidando com uma "redefinição" FORA OU DENTRO. Eu vou fazer isso com a ajuda de constantes calculadas isself , para o qual o cálculo que eu possa usar esse tipo de coisa na Dropsessão a sessão , tanto o seu local de Dragsessão localDragSession . Esta Dragsessão local , por sua vez, possui um contexto local localContext .Se você se lembra, definimos esse contexto local no métodoitemsForVeginning Drag delegado UICollectionViewDragDelegate : Eu estou indo para explorar o contexto local localContext a igualdade na minha coleção CollectionView . Verdadeiro, TYPE de localContext será Any , e preciso fazer uma conversão de TYPE Any usando o operador as ? UICollectionView :
Eu estou indo para explorar o contexto local localContext a igualdade na minha coleção CollectionView . Verdadeiro, TYPE de localContext será Any , e preciso fazer uma conversão de TYPE Any usando o operador as ? UICollectionView : se o contexto local (session.localDragSession? .LocalContext como? UICollectionView) for igual à minha coleção collectionView , a variável calculada isSelf será verdadeirae há um "reset" local dentro da minha coleção. Se essa igualdade for violada, estamos lidando com uma "redefinição"
se o contexto local (session.localDragSession? .LocalContext como? UICollectionView) for igual à minha coleção collectionView , a variável calculada isSelf será verdadeirae há um "reset" local dentro da minha coleção. Se essa igualdade for violada, estamos lidando com uma "redefinição" DropFORA.O método canHandle relata que só podemos lidar com esse tipo de "arrastar e soltar" Dragem nossa coleção Collection View. Caso contrário, mais adiante, não faz sentido falar sobre "dumping" Drop.Se continuarmos a "reset" Drop, ainda é até o momento em que o usuário vai levantar os dedos para fora da tela e não haverá uma "redefinição" real Drop, temos que denunciar iOSusando o método dropSessionDidUpdate delegado UICollectionViewDropDelegate sobre a nossa oferta UIDropProposal para implementar reinicialização Drop.Nesse método, devemos retornar uma Dropsentença que possa ter valores .copy ou .move ou .cancel ou .forbidden para o argumento da operação . E essas são todas as possibilidades que temos no caso comum ao lidar com uma UIView regular .Mas a coleção Collection Viewvai além e oferece o retorno da oferta especializada UICollectionViewDropProposal , que é uma subclassclasse de UIDropProposal e permite especificar, além da operação de operação, um parâmetro de intenção adicional para a coleção Collection View.Parâmetrointenção coleções relatadoCollection Viewde se queremos celular "descartável" colocado dentro de uma célula existente celular e nós queremos adicionar uma nova célula celular .Vidite diferença? No caso da coleção,Collection Viewdevemos comunicar nossa intenção .No nosso caso, sempre queremos adicionar uma nova célula, para que você veja qual será o nosso parâmetro intent .Selecionamos o segundo construtor para UICollectionViewDropProposal : no nosso caso, sempre queremos adicionar uma nova célula e o parâmetro intent assumirá o valor .insertAtDestinationIndexPath em oposição.insertIntoDestinationIndexPath .
no nosso caso, sempre queremos adicionar uma nova célula e o parâmetro intent assumirá o valor .insertAtDestinationIndexPath em oposição.insertIntoDestinationIndexPath . Novamente usei a constante calculada isSelf e, se for uma auto- reorganização, estou movendo .move , caso contrário, estou copiando .copy . Nos dois casos, usamos .insertAtDestinationIndexPath , ou seja, inserir novas células .Até agora, eu não implementei o método performDrop , mas vamos dar uma olhada no que uma coleção já pode fazer
Novamente usei a constante calculada isSelf e, se for uma auto- reorganização, estou movendo .move , caso contrário, estou copiando .copy . Nos dois casos, usamos .insertAtDestinationIndexPath , ou seja, inserir novas células .Até agora, eu não implementei o método performDrop , mas vamos dar uma olhada no que uma coleção já pode fazerCollection Viewcom esse pequeno pedaço de informação que fornecemos a ela.Arrasto a imagem doSafarimecanismo de pesquisaGoogle, e um sinal verde "+" aparece na parte superior desta imagem, indicando que nossa Galeria de imagens não está pronta apenas para aceitar e copiar esta imagem URL, mas também fornece um local dentro da coleção Collection View: posso clicar em outro par de imagens
posso clicar em outro par de imagens Safarie Já haverá três imagens "arrastadas": mas se eu levantar o dedo e soltar
mas se eu levantar o dedo e soltar Dropessas imagens, elas não serão colocadas em nossa Galeria, mas simplesmente retornam aos seus lugares anteriores, porque ainda não implementamos o método performDrop . Você pode ver que a coleção
Você pode ver que a coleção Collection Viewjá sabe o que eu quero fazer.A coleção Collection Viewé uma coisa absolutamente maravilhosa para o mecanismo.Drag & Drop, ela tem uma funcionalidade muito poderosa para isso. Nós mal a tocamos escrevendo 4 linhas de código, e ela já se moveu bastante na percepção de "redefinir" Drop.Vamos voltar ao código e implementar o método performDrop . Nesse método, não seremos capazes de sobreviver com quatro linhas de código, porque o método performDrop é um pouco mais complicado, mas não muito.Quando a "redefinição" ocorre
Nesse método, não seremos capazes de sobreviver com quatro linhas de código, porque o método performDrop é um pouco mais complicado, mas não muito.Quando a "redefinição" ocorre Drop, no método performDrop , precisamos atualizar nosso Model, que é a galeria de imagens imageGallery com uma lista de imagens , e precisamos atualizar nossa coleção visual collectionView .Temos dois cenários diferentes de "redefinição" Drop.Se houver uma “redefinição” Dropda minha coleção collectionView , eu tenho que “redefinir” o Dropelemento da coleção em um novo local e removê-lo do local antigo, pois nesse caso movo ( .move ) esse elemento da coleção. Esta é uma tarefa trivial.Há um "Reset" Dropé realizada de outro aplicativo, então precisamos usar a propriedade itemProvider «arrastado» elemento artigo para buscar dados.Quando executamos uma “redefinição” Dropna coleção collectionView , a coleção nos fornece um coordenador coordenador. Em primeiro lugar, nós informamos o coordenador coordenador , ele destinationIndexPath , ou seja indexPath "-destination", "reset" Drop, que é onde nós será "redefinido". Mas destinationIndexPath pode ser nulo , pois você pode arrastar a imagem "descartada" para a parte da coleção
Mas destinationIndexPath pode ser nulo , pois você pode arrastar a imagem "descartada" para a parte da coleção Collection Viewque não é o local entre algumas células existentes , portanto pode ser nulo . Se há a situação, então eu criar IndexPath com o elemento 0 artigo na th seção do 0 seção chamada . Eu poderia escolher qualquer outro indexPath , mas usarei esse indexPath por padrão.Agora sabemos onde iremos executar o "reset"
Eu poderia escolher qualquer outro indexPath , mas usarei esse indexPath por padrão.Agora sabemos onde iremos executar o "reset" Drop. Temos que passar por todos os itens de coordenador "redefiníveis" fornecidos pelo coordenador . Cada item desta lista possui um UICollectionViewDropItem TYPE e pode nos fornecer informações muito interessantes.Por exemplo, se eu conseguir sourceIndexPath de item.sourceIndexPath , eu vou saber exatamente o que é "arrastar e soltar" Dragé feito por si só, a auto, e a origem do arrasto Dragé o item de coleção com indexPath igual a sourceIndexPath : nem mesmo preciso procurar localContext nesse caso para descobrir que esse "arrastar e soltar" foi feito DENTRO da coleção collectionView .
nem mesmo preciso procurar localContext nesse caso para descobrir que esse "arrastar e soltar" foi feito DENTRO da coleção collectionView . Uau!
Agora eu conheço o sourceIndexPath de origem e o destinationIndexPath de " destino " Drag & Drop, e a tarefa se torna trivial. Tudo o que preciso fazer é atualizar o Model para que a origem e o "destino" sejam trocados e, em seguida, atualizar a coleção collectionView , na qual você precisará remover o item da coleção com sourceIndexPath e adicioná-lo à coleção com destinationIndexPath .Nosso caso local é o mais simples, porque, nesse caso, o mecanismo Drag & Dropfunciona não apenas no mesmo aplicativo, mas na mesma coleção collectionView , e posso obter todas as informações necessárias usando o coordenador coordenador. Vamos implementá-lo neste caso local mais simples: no nosso caso, nem preciso do localObject , que "escondi" anteriormente quando criei o dragItem e que agora posso emprestar do item "arrastado" na coleção de itens no formulário item.localObject . Necessitaremos disso ao “descarregar” as
no nosso caso, nem preciso do localObject , que "escondi" anteriormente quando criei o dragItem e que agora posso emprestar do item "arrastado" na coleção de itens no formulário item.localObject . Necessitaremos disso ao “descarregar” as Dropimagens na “lixeira”, que está no mesmo aplicativo, mas não é a mesma coleção collectionView . Dois IndexPathes são suficientes para mim agora : o sourceIndexPath de origem e o destinationIndexPath de " destino " .Eu recebo informações primeiroimageInfo sobre a imagem no local antigo do Modelo, removendo-a de lá. E, em seguida, inserir em uma série de imagens da minha modelos ImageGallery informações ImageInfo uma imagem com um novo índice destinationIndexPath.item . Foi assim que atualizei meu modelo: agora tenho que atualizar a própria coleção collectionView . É muito importante entender que não quero sobrecarregar todos os dados da minha collectionView com reloadData () no meio do processo de "arrastar e soltar"
agora tenho que atualizar a própria coleção collectionView . É muito importante entender que não quero sobrecarregar todos os dados da minha collectionView com reloadData () no meio do processo de "arrastar e soltar"Drag, porque ele reinstala todo o "mundo" da nossa Galeria de imagens, o que é muito ruim, NÃO FAÇA. Em vez disso, vou arrumar e inserir elementositens individualmente: excluí o item da coleção collectionView com sourceIndexPath e inseri um novo item de coleção com destinationIndexPath .Parece que esse código funciona muito bem, mas, na realidade, esse código pode "travar" seu aplicativo. O motivo é que você faz inúmeras alterações em sua coleção collectionView e, nesse caso, cada etapa da alteração da coleção precisa ser sincronizada com o Modelo normalmente, o que não é observado em nosso caso, pois realizamos as duas operações ao mesmo tempo: excluir e inserir. Daí a coleção collectionViewestará em algum momento em um estado NÃO sincronizado com o modelo.Mas há muito legal maneira de contorná-la, o que é que a coleção CollectionView tem um método chamado performBatchUpdates , que tem circuito (
excluí o item da coleção collectionView com sourceIndexPath e inseri um novo item de coleção com destinationIndexPath .Parece que esse código funciona muito bem, mas, na realidade, esse código pode "travar" seu aplicativo. O motivo é que você faz inúmeras alterações em sua coleção collectionView e, nesse caso, cada etapa da alteração da coleção precisa ser sincronizada com o Modelo normalmente, o que não é observado em nosso caso, pois realizamos as duas operações ao mesmo tempo: excluir e inserir. Daí a coleção collectionViewestará em algum momento em um estado NÃO sincronizado com o modelo.Mas há muito legal maneira de contorná-la, o que é que a coleção CollectionView tem um método chamado performBatchUpdates , que tem circuito ( closure) e dentro deste circuito I pode colocar qualquer número desses DeleteItems , insertItems , moveItems e tudo que eu quero: agora DeleteItems e insertItems será executada como uma única operação, e nunca será visto a falta de sincronização de seus modelos com uma coleção CollectionView .E, finalmente, a última coisa que precisamos fazer é pedir ao coordenador para implementar e animar o "reset"
agora DeleteItems e insertItems será executada como uma única operação, e nunca será visto a falta de sincronização de seus modelos com uma coleção CollectionView .E, finalmente, a última coisa que precisamos fazer é pedir ao coordenador para implementar e animar o "reset" Drop: assim que você levanta o dedo da tela, a imagem se move, tudo acontece ao mesmo tempo: "reset", a imagem desaparece em um lugar e aparência em outro.Vamos tentar mover a imagem de teste "Veneza" em nossa Galeria de imagens para o final da primeira linha ...
assim que você levanta o dedo da tela, a imagem se move, tudo acontece ao mesmo tempo: "reset", a imagem desaparece em um lugar e aparência em outro.Vamos tentar mover a imagem de teste "Veneza" em nossa Galeria de imagens para o final da primeira linha ... ... e "redefini-la":
... e "redefini-la": como queríamos, ela foi colocada no final da primeira linha.
como queríamos, ela foi colocada no final da primeira linha.Viva!
Tudo funciona!Agora, NÃO trataremos do caso local, ou seja, quando o elemento "reset" vier para FORA, ou seja, de outro aplicativo.Para fazer isso, escrevemos mais no código com relação ao sourceIndexPath . Se não tivermos sourceIndexPath , isso significa que o elemento "redefinível" veio de OUTSIDE e teremos que usar a transferência de dados usando o itemProver do elemento redefinível item.dragItem.itemProvider : se você "arrastar
se você "arrastar Drage soltar" OUTSIDE e "soltar" "Drop, essas informações ficam disponíveis instantaneamente? Não, você seleciona dados da coisa “arrastada” ASSINANTE DE FORMA OCORRIDA. Mas e se a amostra demorar 10 segundos? O que a coleção fará neste momento ollection View? Além disso, os dados podem não chegar na ordem em que foram solicitados. Gerenciar isso não é fácil, e Applepropôs para ollection Vieweste caso uma tecnologia completamente nova para o uso de substitutos Placeholders.Você coloca um Collection Viewespaço reservado em sua coleção Placeholdere a coleção Collection Viewgerencia tudo para você; portanto, tudo o que você precisa fazer quando os dados são finalmente selecionados é solicitar ao espaço reservado que Placeholderchame seu espaço reservadoe diga a ele que você recebeu as informações. Em seguida, atualize o seu modelo e contexto placeholderContext AUTOMATICAMENTE troca celular celular com um espaço reservado Placeholderpara uma das suas células células , o que corresponde ao tipo de dados que você recebeu.Realizamos todas essas ações criando um contexto de espaço reservado placeholderContext que gerencia o espaço reservado Placeholdere que você obtém do coordenador do coordenador , solicitando que você “redefina” Dropo elemento do item para o espaço reservado Placeholder.Usarei o inicializador para o contexto de espaço reservado placeholderContextque "lança" dragItem para um UICollectionViewDropPlaceholder : O objeto que vou "lançar"
O objeto que vou "lançar" Dropé item.dragItem , em que item é um elemento for de um loop, pois podemos lançar Dropmuitos itens de coordenador . Nós os "jogamos" um a um. Então item.dragItem é o que "arrastamos Drage soltamos " Drop. O próximo argumento para essa função é o espaço reservado, e eu o crio usando o inicializador UICollectionViewDropPlaceholder : Para fazer isso, preciso saber ONDE vou inserir o espaço reservado
Para fazer isso, preciso saber ONDE vou inserir o espaço reservadoPlaceholder, ou seja, insertionIndexPath , bem como o identificador da célula reutilizada reuseIdentifier .O argumento insertionIndexPath é obviamente igual a destinationIndexPath ; é o IndexPath para colocar o objeto "arrastado"; é calculado no início do método performDropWith .Agora vamos ver o ID da célula reuseIdentifier . Você deve decidir que tipo de célula celular é o seu localizador Placeholder. O coordenador do coordenador não possui uma célula "pré- empacotada " para o localizadorPlaceholder. É VOCÊ quem deve decidir sobre essa célula . Portanto, o identificador da célula reutilizada reutilização de identificação é solicitado ao seu, storyboardpara que possa ser usado como um PROTOTYPE.Vou chamá-lo de "DropPlaceholderCell", mas basicamente, eu poderia nomear o que quer que seja.Esta é apenas a string String que eu vou usar na minha storyboardpara criar essa coisa.Volte ao nosso storyboarde crie uma célula de célula para o espaço reservado Placeholder. Para fazer isso, basta selecionar uma coleção Collection Viewe inspecioná-la. No primeiro campo, Itemsmudo 1para2. Isso cria imediatamente uma segunda célula para nós, que é uma cópia exata da primeira. Selecionamos nossa nova célula
Selecionamos nossa nova célula ImageCell, definimos o identificador “ DropPlaceholderCell”, excluímos todos os UIelementos de lá , inclusive Image View, já que esse PROTOTYPE é usado quando a imagem ainda não chegou. Nós adicionamos um novo indicador de atividade na Paleta de Objetos Activity Indicator, que será rotacionado, informando aos usuários que espero alguns dados de "redefinição". Também alterar a cor de fundo Backgroundpara entender que quando "Reset" a imagem funciona exatamente essa célula celular como protótipos: Além do tipo de nova célula não deve ser ImageCollectionVewCell, porque não haverá imagens nele. Tornarei essa célula uma célula normal UIollectionCiewCell TYPE , já que não precisamos
Além do tipo de nova célula não deve ser ImageCollectionVewCell, porque não haverá imagens nele. Tornarei essa célula uma célula normal UIollectionCiewCell TYPE , já que não precisamos Outletsde controle: vamos configurar o indicador de atividade
vamos configurar o indicador de atividade Activity Indicatorpara que ele comece a animar desde o início e não precisaria escrever nada no código para iniciá-lo. Para fazer isso, clique na opção Animating: E é tudo. Então, fizemos todas as configurações para essa célula
E é tudo. Então, fizemos todas as configurações para essa célula DropPlaceholderCell, retornamos ao nosso código. Agora temos um ótimo localizador Placeholderpronto para usar .Tudo o que precisamos fazer é obter os dados e, quando os dados são recebidos, basta informar o placeholderContext sobre esse contexto e ele trocará o placeholderPlaceholdere nossa célula "nativa" com dados, e faremos alterações no modelo.Vou carregar um objeto, que será meu item usando o método loadObject (ofClass: UIImage.self) (singular). Eu uso o código item.dragItem.itemProvider fornecedor itemProvider , que irá fornecer elementos de dados tem item de forma assíncrona. É claro que, se o iitemProvider estiver conectado , obteremos o objeto de "redefinição" do iitem fora deste aplicativo. A seguir, é apresentado o método loadObject (ofClass: UIImage.self) (singular): Esse fechamento específico NÃO é realizado em
Esse fechamento específico NÃO é realizado emmain queue. E, infelizmente, tivemos que mudar para o main queueuso de DispatchQueue.main.async {} para "capturar" a proporção da imagem na variável aspectRatio local .Introduzimos realmente duas variáveis locais imageURL e aspectRatio ... ... e as capturamos ao carregar a imagem da imagem e o URL da URL :
... e as capturamos ao carregar a imagem da imagem e o URL da URL : Se as variáveis locais imageURL e aspectRatio não forem nulas , solicitaremos ao contexto do espaço reservado placeholderontext usando o método commitInsertionnos dê a oportunidade de alterar nosso modelo imageGallery :
Se as variáveis locais imageURL e aspectRatio não forem nulas , solicitaremos ao contexto do espaço reservado placeholderontext usando o método commitInsertionnos dê a oportunidade de alterar nosso modelo imageGallery : nessa expressão, temos insertionIndexPath - este é o indexPath a ser inserido e alteramos nosso modelo imageGallery . É tudo o que precisamos fazer e esse método substituirá automaticamente um espaço reservado
nessa expressão, temos insertionIndexPath - este é o indexPath a ser inserido e alteramos nosso modelo imageGallery . É tudo o que precisamos fazer e esse método substituirá automaticamente um espaço reservado Placeholderpor uma célula chamando o método cellForItemAt normal .Observe que insertionIndexPath pode ser muito diferente de destinationIndexPath . Porque
Como a amostragem de dados pode levar 10 segundos, é claro, é improvável, mas pode levar 10 segundos. Durante esse período, Collection Viewmuita coisa pode acontecer na coleção . Pode adicionar novas células células , tudo acontece rápido o suficiente.SEMPRE use insertionIndexPath e ONLY insertionIndexPath para atualizar seu modelo.Como atualizamos nosso modelo?Vamos inserir a estrutura imageModel na matriz imageGallery.images , composta pela proporção da imagem aspectRatio e pelo URL da imagem imageURL que o provedor correspondente nos retornou .Isso atualiza nosso modelo imageGallery , e o método commitInsertion faz o resto para nós. Você não precisa mais fazer nada extra, sem inserções, sem exclusões, nada disso. E, é claro, como estamos encerrando, precisamos nos adicionar . .
 Se formos por algum motivo não capaz de obter a relação de aspecto aspectRatio e
Se formos por algum motivo não capaz de obter a relação de aspecto aspectRatio e URLimagem imageURL do correspondente pelo fornecedor , um erro pode ter sido recebido de erro em vez de provedor , temos que deixá-los conhecer o contexto placeholderContext , você precisa destruir este um espaço reservado Placeholder, porque somos todos iguais, não podemos obtenha outros dados: lembre-se de
lembre-se de URLsque eles vêm de lugares como este Google; na realidade, precisam de pequenas transformações para ficarem "limpos"URLpara a imagem. Como esse problema é resolvido pode ser visto neste aplicativo de demonstração em um arquivo Utilities.swiftno Github .Portanto, ao receber uma URLimagem, usamos a propriedade imageURL da classe URL : e é tudo o que você precisa fazer para aceitar FORA DE ALGO dentro da coleção
e é tudo o que você precisa fazer para aceitar FORA DE ALGO dentro da coleção Collection View.Vamos vê-lo em ação. Lançamos simultaneamente, no modo multitarefa, nosso aplicativo de demonstração ImageGallerye Safaricom um mecanismo de pesquisa Google. Como Googleolhamos para as imagens sobre o tema da "Dawn» (nascer do sol). Em Safarijá construídoDrag & Dropo mecanismo, para que possamos selecionar uma dessas imagens, mantenha-a por um longo tempo, mova-a um pouco e arraste-a para a nossa Galeria de imagens. A presença de um sinal de adição verde "+" indica que nosso aplicativo está pronto para aceitar uma imagem de terceiros e copiá-la para sua coleção no local especificado pelo usuário. Depois de o "redefinirmos", leva algum tempo para baixar a imagem e, neste momento, funciona
A presença de um sinal de adição verde "+" indica que nosso aplicativo está pronto para aceitar uma imagem de terceiros e copiá-la para sua coleção no local especificado pelo usuário. Depois de o "redefinirmos", leva algum tempo para baixar a imagem e, neste momento, funciona Placeholder: Depois que o download é concluído, a imagem "redefinida" é colocada no lugar certo e
Depois que o download é concluído, a imagem "redefinida" é colocada no lugar certo e Placeholderdesaparece: podemos continuar a "redefinir" as imagens e colocá-las em nosso coleções de ainda mais imagens:
podemos continuar a "redefinir" as imagens e colocá-las em nosso coleções de ainda mais imagens: após o trabalho de "redefinição"
após o trabalho de "redefinição" Placeholder: Como resultado, nossa Galeria de imagens é preenchida com novas imagens:
Como resultado, nossa Galeria de imagens é preenchida com novas imagens: Agora que está claro que somos capazes de tirar fotos do lado de fora, nós não precisamos da imagem de teste, e vamos remover:
Agora que está claro que somos capazes de tirar fotos do lado de fora, nós não precisamos da imagem de teste, e vamos remover: Nossa viewDidLoad torna-se muito simples: é que estamos a fazer o nosso
Nossa viewDidLoad torna-se muito simples: é que estamos a fazer o nosso Controller Drage Dropdelegar e adicionar reconhecedor gesto pitada , que regula o número de imagens por linha: Claro , podemos adicionar um cache para imagens imageCache :
Claro , podemos adicionar um cache para imagens imageCache : preencheremos imageCache quando "redefinir"
preencheremos imageCache quando "redefinir" Dropno método performDrop ... e quando buscarmos na "rede" na classe ImageCollectionViewCell personalizada :
e quando buscarmos na "rede" na classe ImageCollectionViewCell personalizada : E usaremos o cache imageCache ao reproduzir uma célulacélula da nossa Galeria de imagens na classe personalizada ImageCollectionViewCell :
E usaremos o cache imageCache ao reproduzir uma célulacélula da nossa Galeria de imagens na classe personalizada ImageCollectionViewCell : Agora começamos com uma coleção vazia ...
Agora começamos com uma coleção vazia ... ... então " soltamos " uma nova imagem em nossa coleção ...
... então " soltamos " uma nova imagem em nossa coleção ... ... a imagem é carregada e
... a imagem é carregada ePlaceholderfunciona ... ... e a imagem aparece no lugar certo:
... e a imagem aparece no lugar certo: continuamos a preencher nossa coleção FORA:
continuamos a preencher nossa coleção FORA: Vem carregando imagens e
Vem carregando imagens ePlaceholdersfuncionando ... E as imagens aparecem no lugar certo:
E as imagens aparecem no lugar certo: assim, podemos fazer muito com a nossa Galeria de imagens: preenchê-la FORA, reorganizar itens DENTRO, compartilhar imagens com outras aplicações niyami.Nós apenas precisamos ensiná-la a se livrar de imagens desnecessárias “redefinindo” elas
assim, podemos fazer muito com a nossa Galeria de imagens: preenchê-la FORA, reorganizar itens DENTRO, compartilhar imagens com outras aplicações niyami.Nós apenas precisamos ensiná-la a se livrar de imagens desnecessárias “redefinindo” elasDropna "lixeira" apresentada na barra de navegação à direita. Conforme descrito na seção "Recursos do aplicativo de demonstração da Galeria de imagens", a "lixeira" é representada pela classe GabageView , que herda do UIView, e devemos ensiná-lo a aceitar imagens de nossa coleção ollection View.Redefina as Dropimagens da Galeria para a lixeira.
Imediatamente do local - para a pedreira. Vou acrescentar a GabageView "interação" Interação e vai UIDropInteraction , desde que eu estou tentando "reset" Dropalgumas coisas. Tudo o que temos para garantir que UIDropInteraction , este delegado o delegado , e eu estou indo para nomear a si mesmo, auto , este delegado o delegado : Claro, nossa classe GabageView para confirmar que estamos implementando um protocolo UIDropInteractionDelegate :
Claro, nossa classe GabageView para confirmar que estamos implementando um protocolo UIDropInteractionDelegate : Tudo o que precisamos fazer para fazê-lo funcionar
Tudo o que precisamos fazer para fazê-lo funcionar Drop, isso é para implementar os métodos canHandle já conhecidos por nós ,sessionDidUpdate e performDrop . No entanto, diferentemente dos métodos semelhantes para a coleta
No entanto, diferentemente dos métodos semelhantes para a coletaCollection View, não temos informações adicionais na forma de um caminho de índice do local de dumping.Vamos implementar esses métodos.Dentro do método canHandle serão processados apenas o "drag and drop"Drag, que representam a imagem de um UIImage . Portanto, retornarei true somente se session.canLoadObjects (ofClass: UIImage.self) : no método canHandle ,você basicamente diz apenas que, se o objeto "arrastável" não for uma imagem UIImage, além disso, não faz sentido continuar a opção "redefinir" Soltar e chamar os métodos subsequentes.Se o objeto "arrastável" for uma imagem UIImage , executaremos o método sessionDidUpdate . Tudo o que precisamos fazer neste método é retornar nossa oferta de "redefinição" do UIDropProposal
no método canHandle ,você basicamente diz apenas que, se o objeto "arrastável" não for uma imagem UIImage, além disso, não faz sentido continuar a opção "redefinir" Soltar e chamar os métodos subsequentes.Se o objeto "arrastável" for uma imagem UIImage , executaremos o método sessionDidUpdate . Tudo o que precisamos fazer neste método é retornar nossa oferta de "redefinição" do UIDropProposalDrop . E estou pronto para aceitar apenas o objeto LOCALLY "arrastado" do UIImage TYPE da imagem , que pode ser "descartado" em Dropqualquer lugar dentro do meu GarbageView . Meu GarbageView não interage com imagens despejadas FORA. Então, eu estou analisando usando a variável session.localDragSessionse existe uma “redefinição” local Drope eu retorno a sentença “redefinir” na forma do construtor UIDropProposal com o argumento de operação assumindo o valor .copy , porque SEMPRE LOCAL “arrastar e soltar” Dragno meu aplicativo virá da coleção Collection View. Se "arrastar Drage soltar" e "redefinir" Dropocorrerem FORA, então retornarei a sentença "redefinir" na forma do construtor UIDropProposal com o argumento de operação assumindo o valor .fobbiden , ou seja, "proibido" e obteremos um sinal de proibição de "redefinição" em vez do sinal de mais verde . Copiando uma imagem de UIImage, simularemos uma diminuição em sua escala para quase 0 e, quando ocorrer a "redefinição", removeremos esta imagem da coleção
Copiando uma imagem de UIImage, simularemos uma diminuição em sua escala para quase 0 e, quando ocorrer a "redefinição", removeremos esta imagem da coleção Collection View.Para criar o usuário a ilusão de "reset, eo desaparecimento de" imagem na "lata de lixo", usamos um novo método para nós previewForDropping , que lhe permite redirecionar o "reset" Dropem outro lugar e ao mesmo tempo transformar o objeto "descartável" durante a animação: Em Nesse método, usando o inicializador UIDragPreviewTarget, obtemos um novo préView para o objeto de destino a ser descartado e o redirecionamos usando o método retargetedPreviewpara um novo local, para a “lata de lixo”, com sua escala quase zero:
Em Nesse método, usando o inicializador UIDragPreviewTarget, obtemos um novo préView para o objeto de destino a ser descartado e o redirecionamos usando o método retargetedPreviewpara um novo local, para a “lata de lixo”, com sua escala quase zero: se o usuário levantar um dedo, uma “redefinição” ocorrerá
se o usuário levantar um dedo, uma “redefinição” ocorrerá Drope eu (como o GarbageView ) recebo uma mensagem performDrop . Na mensagem performDrop, realizamos o "reset" real Drop. Honestamente, a imagem que foi despejada no GarbageView em si não nos interessa mais, já que a tornamos praticamente invisível, provavelmente o próprio fato da conclusão da “redefinição” Dropsinalizará que removemos essa imagem da coleção Collection View. Para fazer isso, precisamos conhecer a própria coleção e o indexPath da coleçãoimagem descartada nele. De onde podemos obtê-los?Como o processo Drag & Dropocorre em uma única aplicação, ele está disponível para todos nós o local: local de Dragsessão localDragSession nossa Dropsessão a sessão , o contexto local localContext , que é a nossa colecção de sollectionView e objeto local localObject imagem de reset, o que podemos fazer por si só é imagem de "Galeria" ou indexPath . Devido a isso, podemos obter no método performDrop classe GarbageView coleção coleção , e usá-lodataSource como ImageGalleryCollectionViewController e Modelo ImageGallery nossaController, podemos obter uma série de imagens de imagens TYPE [ImageModel]: Com a ajuda do local de
Com a ajuda do local deDragsessão localDragSession nossaDropsessão sessão fomos capazes de obter todo o "arrastar" na GarbageView Drag elementos de itens , e pode haver um monte, como sabemos, e todos eles são imagens da nossa coleção collectionView . CriandoDragelementos dragItems nossa coleçãoCollection View, nós fornecemos para cada "arrastar"DragelementodragItem objeto local localObject , que é a imagem de imagem , mas é que não vêm a calhar durante a coleta de reorganização interna CollectionView , mas o "reset" Galerias de Imagem "lata de lixo" que precisam desesperadamente nas instalações locais localObject objeto "arrastar" dragItem , após todo esse tempo não temos um coordenador que compartilhe tão generosamente informações sobre o que está acontecendo na coleção collectionView . Portanto, queremos que o objeto local localObject índice foi indexPath na matriz imagem imagens dos nossos modelosimageGallery . Faça as alterações necessárias no método dragItems (a indexPath: IndexPath) classe ImageGalleryCollectionViewController : Agora que podemos tomar todas as "pretaskivaemogo" elemento artigo que localObject , que é o índice indexPath na matriz imagem imagens dos nossos modelos imagegallery , e enviá-lo para os índices de matriz índices e matriz de indexPahes de imagens excluídas:
Agora que podemos tomar todas as "pretaskivaemogo" elemento artigo que localObject , que é o índice indexPath na matriz imagem imagens dos nossos modelos imagegallery , e enviá-lo para os índices de matriz índices e matriz de indexPahes de imagens excluídas: conhecendo a matriz de índices de índices e a matriz de indexPahes de imagens excluídas, no método performBatchUpdatescoleção coleção que remover todas as imagens apagadas a partir de modelos imagens e da coleção da coleção :
conhecendo a matriz de índices de índices e a matriz de indexPahes de imagens excluídas, no método performBatchUpdatescoleção coleção que remover todas as imagens apagadas a partir de modelos imagens e da coleção da coleção : Execute o aplicativo, preencher a galeria com novas imagens:
Execute o aplicativo, preencher a galeria com novas imagens: Selecione um par de imagens que deseja remover de nossa galeria ...
Selecione um par de imagens que deseja remover de nossa galeria ... ... "jogar"-los no ícone com a "lata de lixo" ...
... "jogar"-los no ícone com a "lata de lixo" ... Eles reduziram quase para 0 ...
Eles reduziram quase para 0 ... ... e desaparece da coleção
... e desaparece da coleção Collection View, ocultando-se na "lixeira":
Salvando imagens entre as partidas.
Para salvar a Galeria de Imagens entre as execuções, usaremos UserDefaults , depois de converter nosso Modelo em um JSONformato. Para fazer isso, vamos adicionar à nossa Controllervariável defailts var ... ... e no modelo ImageGallery e ImageModel protocolo codificável :
... e no modelo ImageGallery e ImageModel protocolo codificável : Cordas Cordas , matrizes de Array , o URL , e duas vezes já estão implementando o protocolo codificável , então não temos mais nada a fazer para chegar a codificação de trabalho e decodificação para modelos ImageGallery em
Cordas Cordas , matrizes de Array , o URL , e duas vezes já estão implementando o protocolo codificável , então não temos mais nada a fazer para chegar a codificação de trabalho e decodificação para modelos ImageGallery em JSONformato.Como obtemos a JSONversão do ImageGallery ?Para criar esta variável expandida var json , que retorna o resultado de tentativas de converter-se, auto , via JSONEncoder.encode () no JSONformato: E isso é tudo. Os Dados serão retornados como resultado da conversão automática para o formato
E isso é tudo. Os Dados serão retornados como resultado da conversão automática para o formato JSON, ou nulo se essa conversão falhar, embora a última nunca ocorra, porque esse TIPO é 100% codificável . A variável json opcional é usada apenas por razões de simetria.Agora temos uma maneira de converter os modelos ImageGallery para o formato de dadosJSON . A variável json possui um TYPE Data? que pode ser lembrado em UserDefaults .Agora imagine que, de alguma forma, conseguimos obter os JSONdados do json , e eu gostaria de recriar a partir deles o nosso Model, uma instância da estrutura ImageGallery . Para fazer isso, é muito fácil escrever um INITIALIZER para ImageGallery , cujo argumento de entrada é jsonJSON data . Este inicializador será um inicializador "em queda" (failable) Se ele falhar ao inicializar, ele trava e retorna nulo : Acabei de obter o valor newValue usando o decodificador JSONDecoder , tentando decodificar os dados json que são passados ao meu inicializador e atribuí-lo a si próprio .Se eu consegui fazer isso, recebo uma nova instância do ImageGallery , mas se minha tentativa falhar, retornarei nulo , pois minha inicialização "falhou".Devo dizer que aqui temos muito mais motivos para “falhar” (
Acabei de obter o valor newValue usando o decodificador JSONDecoder , tentando decodificar os dados json que são passados ao meu inicializador e atribuí-lo a si próprio .Se eu consegui fazer isso, recebo uma nova instância do ImageGallery , mas se minha tentativa falhar, retornarei nulo , pois minha inicialização "falhou".Devo dizer que aqui temos muito mais motivos para “falhar” ( fail), porque é bem possível que os JSONdados jsonpode estar estragado ou vazio, tudo isso pode levar à "queda" ( fail) do inicializador.Agora podemos implementar a leitura JSONde dados e modelo de recuperação imagegallery o método viewWillAppear nosso Controller... ... bem como uma entrada no observador didSet {} propriedades imagegallery :
... bem como uma entrada no observador didSet {} propriedades imagegallery : Vamos executar o aplicativo e encher a nossa galeria de imagens:
Vamos executar o aplicativo e encher a nossa galeria de imagens: Se fechar o aplicativo e abri-lo novamente, podemos ver nossa galeria anterior Imagens salvas nos padrões do usuário .
Se fechar o aplicativo e abri-lo novamente, podemos ver nossa galeria anterior Imagens salvas nos padrões do usuário .Conclusão
Este artigo demonstra como é fácil integrar a tecnologia Drag & Dropa um iOSaplicativo usando o exemplo de um aplicativo de demonstração muito simples “Galeria de Imagens” . Isso tornou possível editar completamente a Galeria de Imagens, “lançando” novas imagens de outros aplicativos para lá, movendo as existentes e excluindo as desnecessárias. E também para distribuir imagens acumuladas na Galeria para outros aplicativos.Obviamente, gostaríamos de criar muitas dessas coleções temáticas e pitorescas de imagens e salvá-las diretamente no iPad ou no iCloud Drive. Isso pode ser feito se cada Galeria for interpretada como um UIDocument permanentemente armazenado. Essa interpretação nos permitirá subir para o próximo nível de abstração e criar um aplicativo que funcione com documentos. Nesse aplicativo, seus documentos serão mostrados pelo componente DocumentBrowserViewController , muito semelhante ao aplicativo Files. Isso permitirá que você crie imagens UIDocument do tipo “Galeria de imagens” no seu iPade em iCloud Drive, assim como selecione o documento desejado para visualização e edição.Mas este é o assunto do próximo artigo.PS O código do aplicativo de demonstração antes da implementação do mecanismo Drag & Drope depois está no Github .