
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 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 View
capacidade de "arrastar" Drag
partir dela UIImage imagens externamente e localmente, - ensinaremos nossa coleção de imagens do modo de
Collection View
de Collection View
a aceitar o Drag
"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 View
local e removê-las da Collection 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” Drag
localmente, 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" Drag
fora de nossa coleção Collection View
para outros aplicativos, mas se "arrastarmos" Drag
localmente, 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" Drag
foi cancelado.
Além de objeto local localObject , você pode se lembrar do contexto local localContext para a nossa Drag
sessão a sessão . No nosso caso, será uma coleção de CollectionView e é útil para nós depois:
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 arrastarDrag
muitos 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 Drag
e 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 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 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 Photos
:
Como o Photos
mecanismo já está embutido no aplicativoDrag & Drop
então tudo funciona muito bem e é legal.Portanto, o "arrastar" Drag
e o "despejo" da Drop
imagem 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 Drop
imagens para a coleçãoCollection View
Agora precisamos fazer uma Drop
parte da minha coleção Collection View
para que possamos "despejar" Drop
qualquer 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 :
Assim que adicionamos nosso novo protocolo, o compilador novamente começou a "reclamar" de que não implementamos esse protocolo. Clicamos no botão Fix
e 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á Drop
. Na verdade, vou implementar o método performDrop por último, porque existem alguns outros Apple
métodos altamente recomendados que você precisa implementar para a Drop
peç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 ollection View
e, 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 View
parece 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 Drop
imagem na minha coleção Collection View
FORA.Se o "reset" Drop
a 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 Drop
imagem ocorrer FORA, então devemos manipular apenas o "arrastar e soltar" Drag
que representa a imagem UIImage junto com URL
esta 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 Drop
sessão a sessão , tanto o seu local de Drag
sessão localDragSession . Esta Drag
sessã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 :
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" Drop
FORA.O método canHandle relata que só podemos lidar com esse tipo de "arrastar e soltar" Drag
em 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 iOS
usando o método dropSessionDidUpdate delegado UICollectionViewDropDelegate sobre a nossa oferta UIDropProposal para implementar reinicialização Drop
.Nesse método, devemos retornar uma Drop
sentenç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 View
vai além e oferece o retorno da oferta especializada UICollectionViewDropProposal , que é uma subclass
classe 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 View
de 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 View
devemos 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 .
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 View
com esse pequeno pedaço de informação que fornecemos a ela.Arrasto a imagem doSafari
mecanismo 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 Safari
e Já haverá três imagens "arrastadas":
mas se eu levantar o dedo e soltar Drop
essas 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 Collection View
já 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 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” Drop
da minha coleção collectionView , eu tenho que “redefinir” o Drop
elemento 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” Drop
na 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 Collection View
que 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" 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 . 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 & Drop
funciona 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 Drop
imagens 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"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 ( 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" 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 ...
... e "redefini-la":
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 Drag
e 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 Apple
propôs para ollection View
este caso uma tecnologia completamente nova para o uso de substitutos Placeholders
.Você coloca um Collection View
espaço reservado em sua coleção Placeholder
e a coleção Collection View
gerencia tudo para você; portanto, tudo o que você precisa fazer quando os dados são finalmente selecionados é solicitar ao espaço reservado que Placeholder
chame 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 Placeholder
para 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 Placeholder
e que você obtém do coordenador do coordenador , solicitando que você “redefina” Drop
o 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" Drop
é item.dragItem , em que item é um elemento for de um loop, pois podemos lançar Drop
muitos itens de coordenador . Nós os "jogamos" um a um. Então item.dragItem é o que "arrastamos Drag
e 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 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, storyboard
para 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 storyboard
para criar essa coisa.Volte ao nosso storyboard
e crie uma célula de célula para o espaço reservado Placeholder
. Para fazer isso, basta selecionar uma coleção Collection View
e inspecioná-la. No primeiro campo, Items
mudo 1
para2
. Isso cria imediatamente uma segunda célula para nós, que é uma cópia exata da primeira.
Selecionamos nossa nova célula ImageCell
, definimos o identificador “ DropPlaceholderCell
”, excluímos todos os UI
elementos 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 Background
para 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 Outlets
de controle:
vamos configurar o indicador de atividade Activity Indicator
para 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 DropPlaceholderCell
, retornamos ao nosso código. Agora temos um ótimo localizador Placeholder
pronto 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 placeholderPlaceholder
e 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 emmain queue
. E, infelizmente, tivemos que mudar para o main queue
uso 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 :
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 Placeholder
por 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 View
muita 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 URL
imagem 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 URLs
que eles vêm de lugares como este Google
; na realidade, precisam de pequenas transformações para ficarem "limpos"URL
para a imagem. Como esse problema é resolvido pode ser visto neste aplicativo de demonstração em um arquivo Utilities.swift
no Github .Portanto, ao receber uma URL
imagem, usamos a propriedade imageURL da classe URL :
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 ImageGallery
e Safari
com um mecanismo de pesquisa Google
. Como Google
olhamos para as imagens sobre o tema da "Dawn» (nascer do sol). Em Safari
já construídoDrag & Drop
o 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 Placeholder
:
Depois que o download é concluído, a imagem "redefinida" é colocada no lugar certo e Placeholder
desaparece:
podemos continuar a "redefinir" as imagens e colocá-las em nosso coleções de ainda mais imagens:
após o trabalho de "redefinição" Placeholder
:
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:
Nossa viewDidLoad torna-se muito simples: é que estamos a fazer o nosso Controller
Drag
e Drop
delegar e adicionar reconhecedor gesto pitada , que regula o número de imagens por linha:
Claro , podemos adicionar um cache para imagens imageCache :
preencheremos imageCache quando "redefinir" Drop
no método performDrop ...
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 :
Agora começamos com uma coleção vazia ...
... então " soltamos " uma nova imagem em nossa coleção ...
... a imagem é carregada ePlaceholder
funciona ...
... e a imagem aparece no lugar certo:
continuamos a preencher nossa coleção FORA:
Vem carregando imagens ePlaceholders
funcionando ...
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” elasDrop
na "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 Drop
imagens 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" Drop
algumas 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 :
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 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 UIDropProposalDrop
. E estou pronto para aceitar apenas o objeto LOCALLY "arrastado" do UIImage TYPE da imagem , que pode ser "descartado" em Drop
qualquer 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 Drop
e 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” Drag
no meu aplicativo virá da coleção Collection View
. Se "arrastar Drag
e soltar" e "redefinir" Drop
ocorrerem 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 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" Drop
em 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:
se o usuário levantar um dedo, uma “redefinição” ocorrerá Drop
e 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” Drop
sinalizará 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 & Drop
ocorre em uma única aplicação, ele está disponível para todos nós o local: local de Drag
sessão localDragSession nossa Drop
sessã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 deDrag
sessão localDragSession nossaDrop
sessã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 . CriandoDrag
elementos dragItems nossa coleçãoCollection View
, nós fornecemos para cada "arrastar"Drag
elementodragItem 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:
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:
Selecione um par de imagens que deseja remover de nossa galeria ...
... "jogar"-los no ícone com a "lata de lixo" ...
Eles reduziram quase para 0 ...
... 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 JSON
formato. Para fazer isso, vamos adicionar à nossa Controller
variável defailts var ...
... 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 JSON
formato.Como obtemos a JSON
versã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 JSON
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 JSON
dados 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” ( fail
), porque é bem possível que os JSON
dados jsonpode estar estragado ou vazio, tudo isso pode levar à "queda" ( fail
) do inicializador.Agora podemos implementar a leitura JSON
de dados e modelo de recuperação imagegallery o método viewWillAppear nosso Controller
...
... bem como uma entrada no observador didSet {} propriedades imagegallery :
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 .Conclusão
Este artigo demonstra como é fácil integrar a tecnologia Drag & Drop
a um iOS
aplicativo 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 iPad
e 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 & Drop
e depois está no Github .