Arraste e solte nos seus aplicativos para iOS



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:

  1. primeiro, dotaremos nossa Collection View capacidade de "arrastar" Drag partir dela UIImage imagens externamente e localmente,
  2. ensinaremos nossa coleção de imagens do modo de Collection View de Collection View a aceitar o Drag "arrastado" externa ou localmente do UIImage ,
  3. 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” 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 Dragsessã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 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 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 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 :



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á 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 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 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 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 :



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 .



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 Safarie Já haverá três imagens "arrastadas":



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 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 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 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" 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 & 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 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"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 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" 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 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 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 Outletsde controle:



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 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 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 :



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 Placeholderpor uma célula chamando o método cellForItemAt normal .

Observe que insertionIndexPath pode ser muito diferente de destinationIndexPath . PorqueComo 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 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 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 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 Placeholder:



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:



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 Drage Dropdelegar 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" Dropno 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 ePlaceholderfunciona ...



... e a imagem aparece no lugar certo:



continuamos a preencher nossa coleção FORA:



Vem carregando imagens ePlaceholdersfuncionando ...



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” 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 :



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 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 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:



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 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:



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 JSONformato. Para fazer isso, vamos adicionar à nossa Controllervariá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 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 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” ( 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 :



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 & 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 .


Source: https://habr.com/ru/post/pt425817/


All Articles