Activityindicator Not Showing Ios Shows Once Then Never Again

Visually rich links describe beautiful imagery, typography and even video thumbnails used to preview web folio content. They're a significant upgrade from blue text, which was the only thing bachelor before iOS thirteen.

Regular vs rich links.

In this tutorial, you'll use the LinkPresentation framework to make links better looking, more than engaging and more user friendly. Check out this case of a video thumbnail preview:

Rich link preview with video.

All a web folio has to do is add together a couple of special HTML tags to specify the title, icon, image or video and voilà: Your links come up alive!

Just imagine how overnice it'll be for your messaging, weblog or recipe app to immediately prove a preview of the content the user's about to see. You'd click on the right link much faster than the left, even though they link to the same web page.

Regular vs rich link.

With the addition of the LinkPresentation framework, you can easily and quickly showcase links in your apps. Set up to dive in?

In this tutorial, you'll larn how to:

  • Create rich links.
  • Handle LinkPresentation errors.
  • Store metadata.
  • Optimize the share sheet.
  • Relieve favorite links.

Getting Started

In this tutorial, you'll be working on an app called Raylette. Each time yous spin the wheel, Raylette randomly chooses a raywenderlich.com tutorial and presents it to y'all using the LinkPresentation framework.

Final app screenshots.

Hopefully, it inspires y'all to check out a topic you might not accept come beyond otherwise!

Use the Download Materials button at the top or bottom of this tutorial to download the starter projection. Open up it and build and run. Y'all'll encounter you already accept a Spin the Wheel push button and two tabs: Spin and Saved:

Begin app screenshots

Earlier you swoop into the code, though, at that place'southward a bit of theory to encompass. Then hold on tight!

Understanding Rich Links

Rich links are link previews yous see, for case, when users transport messages through the Letters app.

Depending on the information Apple can extract from the web page, a link preview can look one of these four means:

Various rich link views based on web page tags.

Agreement Rich Links: Spider web Page Metadata

The spider web page'south metadata dictates what you'll see in the preview. Look at the tags in the <head> section. You tin do this in Chrome by right-clicking on a spider web page and choosing View Folio Source.

Context menu in browser View Page Source

Here's an example for the Palpitate Hero Animations tutorial from raywenderlich.com:

<head> <title>Flutter Hero Animations | raywenderlich.com</title> <meta property="og:title" content="Flutter Hero Animations"> <meta holding="og:type" content="video.other"> <meta holding="og:image" content="https://files.betamax.raywenderlich.com/attachments/collections/222/0c45fb39-9f82-406f-9237-fc1a07a7af15.png"> <meta holding="og:description" content="<p>Acquire and understand how to apply the Hero widget in Flutter to animate cute screen transitions for your apps.</p> "> <meta property="og:site_name" content="raywenderlich.com"> <link rel="icon" type="image/png" href="/favicon.png"> ... </head>        

The metadata that powers rich links consists of both Open up Graph meta tags and other HTML tags. The LinkPresentation framework extracts all these tags and uses the most advisable ones.

Agreement Rich Links: Open up Graph Protocol

The Open up Graph protocol is a standard of web page meta tags for visually rich links in apps like Facebook, Twitter or Letters:

Examples of how rich links are presented on Facebook, Twitter and Messages

Befitting to the protocol is pretty elementary. Yous merely demand to add some special <meta> tags in the <head> of your web folio and you'll be up and running in no fourth dimension.

The <meta> tags required past the Open Graph protocol are:

  • og:title: object title
  • og:type: object type, for example music, video, article and many more
  • og:paradigm: the object's prototype URL
  • og:url: the canonical URL of the object

You tin can easily recognize the Open Graph <meta> tags by their og: prefix.

The majority of raywenderlich.com articles and video courses take code similar this. Each web page has the following tags: og:title, og:type, og:prototype, og:clarification and og:site_name.

Note: og:site_name specifies that a particular web folio is part of a larger website. In our example, it tells us Flutter Hero Animations is part of the larger raywenderlich.com website.

Check out the full specifications of the Open Graph protocol to acquire more than near how it works and what types it supports.

Building the Preview

The LinkPresentation framework extracts the metadata from all the web folio's tags and uses the most appropriate ones to display the best preview.

Which tags generate which link previews.

The preview depends on five pieces of information:

Edifice the Preview: The URL

The URL comes from either:

  • The site'southward URL
  • og:site_name similar <meta property="og:site_name" content="raywenderlich.com">

When og:site_name is nowadays, information technology takes precedence over the URL in the link preview. All Open Graph meta tags take precedence over the other alternatives when they're present.

Edifice the Preview: The Title

The title comes from either:

  • <title>Flutter Hero Animations | raywenderlich.com</championship>
  • <meta property="og:title" content="Flutter Hero Animations">

<title> specifies the title of the web page you see in the browser. But sometimes, the <title> tag duplicates the site's URL, as in this example. To avoid this duplication in your preview, use og:title instead. It will take precedence over the <championship> tag.

Apple recommends you:

  1. Keep titles unique and informative.
  2. Avoid duplicating the site proper noun in the championship.
  3. Don't generate tags dynamically, because the LinkPresentation framework doesn't run JavaScript.

Edifice the Preview: The Icon

The icon comes from this tag: <link rel="icon" type="epitome/png" href="/favicon.png">

Building the Preview: The Image

The paradigm comes from this tag: <meta holding="og:image" content="image.png">

Apple recommends you:

  1. Use images specific to your content.
  2. Avert adding text. Rich links appear in many sizes across multiple devices; the paradigm text might not scale.
  3. Specify an icon even when y'all have an paradigm, as a fallback.

Edifice the Preview: The Video

The video comes from this tag: <meta belongings="og:video:url" content="video.mp4">

Apple recommends you:

  1. Keep the size of the icon + image + video to 10MB or less.
  2. Reference video files directly, rather than YouTube links, which volition not autoplay.
  3. Avoid videos that require HTML or plug-ins; they are not supported.

All of these, except the URL itself, are optional. The LinkPresentation framework will e'er choose the "richest" data possible to nowadays the link preview in the all-time way. The lodge of "richness" goes from Video > Image > Icon.

And with that, you're finally set up to jump into the code!

Retrieving the Metadata

The start step to presenting rich links is to get the metadata.

In Xcode, open SpinViewController.swift. Here you'll meet a large array of raywenderlich.com tutorials, some outlets from the storyboard and several methods for you to implement.

To starting time using the LinkPresentation framework, you first have to import it. Place this at the top of the file, right beneath import UIKit:

import LinkPresentation        

To grab the metadata for a given URL, y'all'll utilize LPMetadataProvider. If the fetch is successful, you'll go dorsum LPLinkMetadata, which contains the URL, title, image and video links, if they be. All the properties on LPLinkMetadata are optional considering there's no guarantee the web page has them prepare.

Add a new provider property, right below the last @IBOutlet definition for errorLabel:

private var provider = LPMetadataProvider()        

To fetch the metadata, you'll telephone call startFetchingMetadata(for:completionHandler:) on the provider.

Locate spin(_:) and add together the following implementation:

// Select random tutorial link permit random = Int.random(in: 0..<links.count) allow randomTutorialLink = links[random]  // Copy the provider provider = LPMetadataProvider()  guard allow url = URL(cord: randomTutorialLink) else { render }  // Start fetching metadata provider.startFetchingMetadata(for: url) { metadata, error in   guard      permit metadata = metadata,      mistake == nil      else { return }    // Use the metadata   impress(metadata.championship ?? "No Title") }        

You're probably wondering why you're recreating provider every time the user taps to spin the wheel. Well, LPMetadataProvider is a one-shot object, and then you tin but utilize an case once. If you lot try to reuse it, you'll get an exception like this:

2020-01-12 19:56:17.003615+0000 Raylette[23147:3330742] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Trying to showtime fetching on an LPMetadataProvider that has already started. LPMetadataProvider is a ane-shot object.'        

But, it'southward a good idea to have a class-wide reference to it in instance you lot need to use information technology afterward in other methods.

Build and run and press the spin push a few times to brand sure the URL titles go printed to the console:

Success text in console

Presenting Your Links

It's no fun just printing the championship of the web page to the panel, though. The existent magic of rich links is to render them beautifully in the app!

Presenting a link is quite piece of cake. The LinkPresentation framework includes LPLinkView that does all the heavy lifting for yous.

Add a new property, right below provider:

private var linkView = LPLinkView()        

Each time y'all spin the bike, you'll create a new LPLinkView case with the given URL and add it to stackView. Once you fetch the metadata for that particular URL, you'll add it to linkView.

Supersede the current implementation of spin(_:) with the code beneath:

let random = Int.random(in: 0..<links.count) allow randomTutorialLink = links[random]  provider = LPMetadataProvider() // 1 linkView.removeFromSuperview()  guard let url = URL(cord: randomTutorialLink) else { return } // 2 linkView = LPLinkView(url: url)  provider.startFetchingMetadata(for: url) { metadata, error in   guard      let metadata = metadata,      error == nil      else { return }    // 3   DispatchQueue.principal.async { [weak cocky] in     // 4     baby-sit allow self = self else { return }      self.linkView.metadata = metadata   } } // 5 stackView.insertArrangedSubview(linkView, at: 0)        

In the code above, you:

  1. Remove linkView from stackView, if information technology's already at that place. You only want to present one link at a time.
  2. Initialize linkView with just the URL so while yous're fetching the metadata, the user will still see something displayed.
  3. Assign the metadata to linkView. Then you employ DispatchQueue to process UI changes on the main thread, since the metadata fetching executes on a background thread. If you don't, the app will crash.
  4. Use a reference to the view controller to update the interface in the background. By using [weak cocky] and guard let self = cocky, you ensure the update can go on without causing a retain wheel — no matter what the user does while the background process is running.
  5. Add together linkView to the stack view. This code runs immediately and gives the user something to see (the URL). Then, when the background process completes, it updates the view with the rich metadata.

Build and run and spin the bike to run into the link previews in action!

The app with link previews of image, icon and video links.

Some of the previews take quite a while to load, particularly ones that include video links. Only there's nothing that tells the user the preview is loading, so they have footling incentive to stick around. You'll ready that next.

Adding an Activity Indicator

To meliorate the user experience when waiting for rich links to load, you'll add an activeness indicator below the link view.

To practise that, you'll use UIActivityIndicatorView. Take a look at SpinViewController.swift and detect information technology already has a property chosen activityIndicator. You add this property to stackView at the finish of viewDidLoad().

Start animating activityIndicator by adding this line at the beginning of spin(_:):

activityIndicator.startAnimating()        

Adjacent, replace the block of lawmaking for fetching the metadata with this:

provider.startFetchingMetadata(for: url) { [weak cocky] metadata, error in   baby-sit permit cocky = cocky else { return }    guard      let metadata = metadata,      error == nada      else {       self.activityIndicator.stopAnimating()       return   }    DispatchQueue.main.async { [weak self] in     baby-sit let cocky = self else { return }      self.linkView.metadata = metadata     self.activityIndicator.stopAnimating()   } }        

Afterward unwrapping a couple optional values, this code tells the main queue to update the user interface past stopping the animation and setting the metadata on the linkView.

Build and run to come across how much a unproblematic activity indicator adds to the experience!

Link previews with loading indicator added.

Handling Errors

Thinking further about the user experience, it'd be nice if you let your users know when an fault occurs, so they don't keep spinning the bike in vain.

LPError defines all the errors that can occur if fetching the metadata fails:

  • .cancelled: The customer cancels the fetch.
  • .failed: The fetch fails.
  • .timedOut: The fetch takes longer than allowed.
  • .unknown: The fetch fails for an unknown reason.

If the fetch fails, you'll testify the user why. To do this, yous'll use errorLabel in stackView. It starts subconscious but you'll unhide it and assign it some sensible text based on the error you receive.

Handling Errors: Error Messages

Go to LPError+Extension.swift and replace LPError with this:

extension LPError {   var prettyString: String {     switch self.code {     case .metadataFetchCancelled:       return "Metadata fetch cancelled."     case .metadataFetchFailed:       return "Metadata fetch failed."     case .metadataFetchTimedOut:       return "Metadata fetch timed out."     case .unknown:       return "Metadata fetch unknown."     @unknown default:       render "Metadata fetch unknown."     }   } }        

This extension creates a human-readable fault string for the different LPErrors.

Now go back to SpinViewController.swift and add this at the top of spin(_:):

errorLabel.isHidden = truthful        

This clears out the mistake when the user taps spinButton.

Adjacent, update the fetch block to show the mistake like this:

baby-sit    let metadata = metadata,    error == zero    else {     if allow fault = error as? LPError {       DispatchQueue.chief.async { [weak self] in         guard let self = self else { return }          self.activityIndicator.stopAnimating()         self.errorLabel.text = error.prettyString         self.errorLabel.isHidden = false       }     }     return }        

In the lawmaking above, you cheque for any errors. If one exists, you update the UI on the principal thread to cease the activeness indicator and then display the mistake to the user.

Unfortunately, you can't exam this with the current setup. So, add this to spin(_:), right after the new provider instance:

provider.timeout = i        

This volition trigger an error message when any of the links accept longer than one 2nd to load. Build and run to run into this:

Error handing label when metadata fetch times out.

Y'all set timeout to ane to exam the error message. Bump it upwardly to v now to allow a more reasonable amount of time for these rich previews to load:

provider.timeout = 5        

Note: The default timeout is 30 seconds.

Handling Errors: Cancel Fetch

Your users don't know the fetch will time out at five seconds, and they might non desire to wait longer than two. If it's taking that long, they'd rather cancel the fetch. Y'all'll give them that pick adjacent.

Within the implementation of spin(_:), add this right under errorLabel.isHidden = true:

guard !activityIndicator.isAnimating else {   cancel()   return }  spinButton.setTitle("Abolish", for: .normal)        

First, you make sure activityIndicator isn't spinning. But if it is, yous know:

  1. The user tapped the Spin the Wheel version of the button. This started the fetch and set activityIndicator.isAnimating to true.
  2. The user likewise tapped the Cancel version of the push button because they decided to bond on the fetch.

If and then, you phone call cancel() and return.

Otherwise, if activityIndicator isn't spinning, you know the user only tapped the Spin the Wheel version of the button. Then, before you kick off the fetch, you modify the button title to Abolish, in case they want to cancel the fetch later.

At this signal, cancel() doesn't practise anything. You lot'll set up that next. Replace it with this:

private func cancel() {   provider.cancel()   provider = LPMetadataProvider()   resetViews() }        

Here, you showtime call abolish() on the provider itself. Then you create a new provider example and call resetViews.

But resetViews() doesn't practice anything withal either. Fix that by replacing it with this:

private func resetViews() {   activityIndicator.stopAnimating()   spinButton.setTitle("Spin the Wheel", for: .normal) }        

In the lawmaking higher up, you terminate the activity indicator and gear up the title for spinButton back to "Spin the Cycle":

Also, to get this aforementioned functionality in provider.startFetchingMetadata, supervene upon the 2 instances of self.activityIndicator.stopAnimating() with cocky.resetViews():

self.resetViews()        

Now if you encounter an mistake or the preview loads, you lot'll stop the action indicator and reset the title of spinButton to "Spin the Bicycle".

Build and run. Make sure you tin cancel the asking and that errorLabel shows the right issue.

Cancelling the metadata fetch error.

Storing the Metadata

Information technology tin can get a bit tedious to lookout man these links load, particularly if you become the same consequence dorsum more than once. To speed upward the procedure, you tin cache the metadata. This is a common tactic because spider web page metadata doesn't alter very ofttimes.

And estimate what? You're in luck. LPLinkMetadata is serializable by default, which makes caching it a cakewalk. It also conforms to NSSecureCoding, which you lot'll need to keep in heed when archiving. You tin can acquire about NSSecureCoding in this tutorial.

Storing the Metadata: Cache and Remember

Become to MetadataCache.swift and add these methods to the top of MetadataCache:

static func enshroud(metadata: LPLinkMetadata) {   // Check if the metadata already exists for this URL   practice {     guard call up(urlString: metadata.url!.absoluteString) == nil else {       return     }      // Transform the metadata to a Data object and      // set requiringSecureCoding to truthful     let information = try NSKeyedArchiver.archivedData(       withRootObject: metadata,        requiringSecureCoding: true)      // Relieve to user defaults     UserDefaults.standard.setValue(data, forKey: metadata.url!.absoluteString)   }   catch let error {     print("Error when caching: \(error.localizedDescription)")   } }  static func think(urlString: String) -> LPLinkMetadata? {   do {     // Check if data exists for a particular url string     guard        allow data = UserDefaults.standard.object(forKey: urlString) equally? Information,       // Ensure that it can be transformed to an LPLinkMetadata object       let metadata = try NSKeyedUnarchiver.unarchivedObject(         ofClass: LPLinkMetadata.self,          from: data)        else { return nada }     return metadata   }   take hold of let error {     print("Error when caching: \(mistake.localizedDescription)")     return nil   } }        

Here, you're using NSKeyedArchiver and NSKeyedUnarchiver to transform LPLinkMetadata into or from Data. You use UserDefaults to store and retrieve it.

Notation: UserDefaults is a database included with iOS that you can employ with very minimal setup. Data stored in UserDefaults persists on difficult drive storage fifty-fifty after the user quits your app.

Storing the Metadata: Refactor

Hop back to SpinViewController.swift.

spin(_:) is getting a little long. Refactor information technology by extracting the metadata fetching into a new method called fetchMetadata(for:). Add this code after resetViews():

private func fetchMetadata(for url: URL) {   // 1. Cheque if the metadata exists   if let existingMetadata = MetadataCache.retrieve(urlString: url.absoluteString) {     linkView = LPLinkView(metadata: existingMetadata)     resetViews()   } else {     // 2. If it doesn't start the fetch     provider.startFetchingMetadata(for: url) { [weak cocky] metadata, error in       guard let self = self else { return }        guard          let metadata = metadata,          fault == nil          else {           if allow error = error as? LPError {             DispatchQueue.main.async { [weak self] in               guard permit self = self else { return }                self.errorLabel.text = error.prettyString               cocky.errorLabel.isHidden = imitation               cocky.resetViews()             }           }           render       }        // 3. And cache the new metadata once y'all accept it       MetadataCache.cache(metadata: metadata)        // 4. Use the metadata       DispatchQueue.primary.async { [weak cocky] in         baby-sit let self = self else { return }          cocky.linkView.metadata = metadata         self.resetViews()       }     }   } }        

In this new method, you not only extract the metadata fetching, you as well add the following functionality:

  1. Render linkView and reset the views to normal if metadata exists.
  2. Commencement the fetch if metadata doesn't exist.
  3. Enshroud the results of the fetch.

Next, supervene upon provider.startFetchingMetadata() with a call to your new method. When you lot're washed, y'all'll have the single line calling fetchMetadata() between linkView and stackView:

linkView = LPLinkView(url: url)  // Replace the prefetching functionality fetchMetadata(for: url)  stackView.insertArrangedSubview(linkView, at: 0)        

Build and run to observe how fast your links load. Keep tapping Spin the Wheel until you get a link that has been cached. Find that your links volition load immediately if y'all've seen them earlier!

Showing how fast links load when they're cached.

What's the point of finding all these great tutorials if you lot can't share them with your friends though? Yous'll fix that side by side.

Sharing Links

The LinkPresentation framework has a cool protocol, UIActivityItemSource, which you lot can use to pass LPLinkMetadata direct to the share sail.

That means that instead of reaching out to the server and waiting for the link to load the title and icon asynchronously in the share sail, you tin pre-populate it with the metadata you already fetched.

Share sheet link loading gif.

Now your users will see a rich preview immediately. Information technology'south the little things that count!

Share sheet preview for loaded and not loaded metadata.

Sharing Links: UIActivityItemSource

Commencement, go along track of the metadata by adding a new property to SpinViewController right beneath linkView:

private var currentMetadata: LPLinkMetadata?        

Next, assign the value to currentMetadata in fetchMetadata(for:).

Add this line within of the first if let argument in fetchMetadata(for:):

currentMetadata = existingMetadata        

And add together this line correct before you cache the metadata, preceding the line, MetadataCache.cache(metadata: metadata):

self.currentMetadata = metadata        

Now, to make use of the new LinkPresentation functionality, you lot accept to make SpinViewController conform to UIActivityItemSource.

Add together this extension outside SpinViewController, after the endmost curly brace:

extension SpinViewController: UIActivityItemSource {   // i. Required function returning a placeholder   func activityViewControllerPlaceholderItem(     _ activityViewController: UIActivityViewController   ) -> Any {     render "website.com"   }    // 2. Required part that returns the actual activeness particular   func activityViewController(     _ activityViewController: UIActivityViewController,      itemForActivityType activityType: UIActivity.ActivityType?   ) -> Any? {     return currentMetadata?.originalURL   }    // 3. The metadata that the share sheet automatically picks upward   func activityViewControllerLinkMetadata(     _ activityViewController: UIActivityViewController   ) -> LPLinkMetadata? {     render currentMetadata   } }        

Conforming to UIActivityItemSource requires you to implement iii methods:

  1. The placeholder method should return something close to the real data you intend to show in the Bailiwick field of your activeness detail. Withal, it does not have to incorporate real data and it should render as fast as possible. You lot'll update information technology when the real data finishes loading. For now, a unproblematic text string is sufficient.
  2. The originalURL of currentMetadata allows the view to figure out what type of information it volition display.

    Note: You must return the originalURL property of the metadata because it contains type data that wouldn't exist if yous merely render a new URL() with the same cord. This is easy to get wrong and can create bugs that are hard to track downwardly.

  3. Finally, activityViewControllerLinkMetadata(_:) is where the existent magic happens when you extract all the juicy details from currentMetadata.

Sharing Links: View Update

To display this in the UI, you'll add a share button below the link view once the preview loads. The starter project provides a whole stack view with two activeness buttons on SpinViewController; you merely have to show it!

Showing the activity buttons below the link preview.

The view to show is actionsStackView. When the link metadata is loading, you hide the view. Once the preview is loaded, y'all show it.

Add this line under activityIndicator.startAnimating() within spin(_:):

actionsStackView.isHidden = true        

And unhide it later by adding this to the finish of resetViews(), earlier the closing curly brace:

actionsStackView.isHidden = simulated        

Next, replace share(:) with this:

@IBAction func share(_ sender: Whatever) {   guard currentMetadata != nil else { return }    permit activityController = UIActivityViewController(     activityItems: [self],      applicationActivities: zilch)   present(activityController, blithe: truthful, completion: nil) }        

In the code in a higher place, you:

  1. Cheque if currentMetadata exists. If it does, you create an instance of UIActivityViewController.
  2. Pass it [self] every bit activityItems. This is the important chip as it tells the activity controller to look at how SpinViewController conforms to UIActivityItemSource.

Build and run and tap the share button to see how smooth it is!

Preloaded share sheet.

Note: If yous want to swap the icon that appears in the sail preview, you can utilize code like this in fetchMetadata(for:) correct earlier MetadataCache.cache(metadata: metadata):

if let imageProvider = metadata.imageProvider {   metadata.iconProvider = imageProvider }          

This swaps out iconProvider for imageProvider.

Saving Favorites

Lastly, you want to salvage the tutorials y'all come beyond, because, hey, you might never see them again!

Next to the Share push button y'all implemented in the previous section, there's too a Save button.

Highlighting the save button.

You'll store the tutorials the user wants to save under a special key in UserDefaults called savedURLs and brandish all the tutorials every bit link previews in the Saved tab in a stack view.

Become to MetadataCache.swift and add this underneath your current methods, right earlier the closing curly brace:

// ane static var savedURLs: [Cord] {   UserDefaults.standard.object(forKey: "SavedURLs") as? [String] ?? [] }  // 2 static func addToSaved(metadata: LPLinkMetadata) {   guard var links = UserDefaults.standard     .object(forKey: "SavedURLs") as? [String] else {     UserDefaults.standard.set([metadata.url!.absoluteString], forKey: "SavedURLs")     render   }    guard !links.contains(metadata.url!.absoluteString) else { return }    links.append(metadata.url!.absoluteString)   UserDefaults.standard.set(links, forKey: "SavedURLs") }        

In the code above, you:

  1. Make a new property, savedURLs, which returns the array stored at the SavedURLs key in UserDefaults.
  2. Create addToSaved(metadata:) that you can call to bank check if a URL already exists in the SavedURLs array in UserDefaults and add it to the assortment if information technology does not.

Next, become back to SpinViewController.swift and replace save(_:) with this:

@IBAction func salvage(_ sender: Any) {   guard allow metadata = currentMetadata else { render }   MetadataCache.addToSaved(metadata: metadata)   errorLabel.text = "Successfully saved!"   errorLabel.isHidden = false }        

In the code above, yous cheque for metadata. If any exists, y'all call addToSaved(metadata:) and notify the user of the success through errorLabel.

At present that yous're successfully saving your favorite URLs, information technology'southward fourth dimension to display the links.

Switch over to SavedViewController.swift and replace loadList() with this:

private func loadList() {   // ane   stackView.arrangedSubviews.forEach { $0.removeFromSuperview() }    // 2   allow links = MetadataCache.savedURLs   let metadata = links.compactMap { MetadataCache.retrieve(urlString: $0) }    // 3   metadata.forEach { metadata in     let linkView = LPLinkView(metadata: metadata)     stackView.addArrangedSubview(linkView)   } }        

In the office above, you:

  1. Remove all the link preview subviews from stackView then it's completely empty.
  2. Grab all the links and convert them to LPLinkMetadata objects with retrieve(urlString:).
  3. Add together all the subviews to stackView.

Build and run. Save a couple of the tutorials, and see them announced on the Saved tab of the app!

Saved tab with saved tutorials.

Using UIStackView Versus UITableView

Yous're using UIStackView to show these link previews instead of UITableView. Tabular array views seem like the obvious choice to display information in a listing but they're not the way to become with link presentations. There are 2 big reasons for this:

  1. Sizing: If you follow Apple's advice and telephone call sizeToFit() on the link preview in tableView(_:cellForRowAt:), you'll find the previews don't announced. Y'all could get them to appear with Auto Layout constraints on your custom cell or by giving them a specific CGSize, but read on earlier you try this.
  2. Memory: Fifty-fifty if you can get your previews to appear, you accept another issue: LPLinkViews in tabular array views cause huge retentivity leaks. They can get as high as 10GB when scrolling! Have a look at the retentivity load below:

    Memory issues in a table view.

With a retention leak similar that, your app will crash and the one-star reviews volition start flying. Best to stay away from table views for link presentations!

Time to examination your app i more time. Build and run and tap one of the links in the Saved tab. Safari volition open and y'all'll run across your chosen tutorial displayed every bit a web page. Practiced task!

Where to Get From Hither?

Congrats! You've just learned most of what in that location is to know nearly link presentations!

Download the terminal project using the Download Materials button at the top or bottom of this page.

To learn more virtually what Apple tree has to say on the topic, watch the WWDC 2019 videos on Embedding and Sharing Visually Rich Links and Ensuring Beautiful Rich Links.

If yous want to have a closer look at the framework, read the documentation on the LinkPresentation framework.

And if you're curious how to make links even more powerful and useful, check out our Universal Linking tutorial!

If you have any questions or comments, delight join the forum discussion beneath.

powenoblits.blogspot.com

Source: https://www.raywenderlich.com/7565482-visually-rich-links-tutorial-for-ios-image-thumbnails

0 Response to "Activityindicator Not Showing Ios Shows Once Then Never Again"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel