{"id":374,"date":"2016-05-29T02:22:39","date_gmt":"2016-05-29T02:22:39","guid":{"rendered":"http:\/\/strawberrycode.com\/blog\/?p=374"},"modified":"2016-05-30T20:08:52","modified_gmt":"2016-05-30T20:08:52","slug":"uicollectionview-custom-layout-the-ultimate-tutorial-part-2","status":"publish","type":"post","link":"https:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/","title":{"rendered":"UICollectionView Custom Layout: the Ultimate Tutorial \u2013 Part 2"},"content":{"rendered":"<p>Welcome back to the &#8220;UICollectionView Custom Layouts: the Ultimate Tutorial&#8221; series. This is Part 2, where we will actually tackle the custom layout part.<br \/>\nIf you haven&#8217;t seen or done the <a href=\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-1\/\" target=\"_blank\">first part<\/a>, I strongly recommend you to take a look at it, as we will be using the <code>UICollectionView<\/code> we created there.<\/p>\n<p>Remember what we had at the end of <a href=\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-1\/\" target=\"_blank\">Part 1<\/a>, when we run the project without the custom layout, and what we would like to achieve?<\/p>\n<p><img src=\"https:\/\/raw.githubusercontent.com\/strawberrycode\/SCCollectionViewCustomLayout\/master\/Images\/MyCollectionView_NoLayout.png\" alt=\"CollectionView without UICollectionViewFlowLayout\" class=\"\" style=\"display:inline-block; vertical-align:top\" width=\"280\"\/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src=\"https:\/\/raw.githubusercontent.com\/strawberrycode\/SCCollectionViewCustomLayout\/master\/Images\/Wireframe.png\" alt=\"Wireframe of the CollectionView with UICollectionViewFlowLayout\" class=\"\" style=\"display:inline-block\" width=\"250\"\/><\/p>\n<h2>A few words<\/h2>\n<p>A few words before we start:<\/p>\n<p>I recommend you to have a look at Session 232 of WWDC 2014: <a href=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2014\/232\/\" target=\"_blank\">Advanced User Interfaces with Collection Views<\/a> for the Custom Layout questions, and at Session 226 of WWDC 2014: <a href=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2014\/226\/\" target=\"_blank\">What&#8217;s new in Table and Collection Views<\/a> concerning the cell sizing. I won&#8217;t go into the details here as everything is well explained on the videos.<br \/>\nHere, we will focus on the implementation.<\/p>\n<h2>Preparing the cells<\/h2>\n<p>First, we need to add a bit of code in the <code>TextCell<\/code> class, as this cell will need to get resized. Simply paste the following code in <code>TextCell.swift<\/code><\/p>\n<pre class=\"lang:swift decode:true \" title=\"TextCell - preferredLayoutAttributesFittingAttributes\" >override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -&gt; UICollectionViewLayoutAttributes {\r\n        \r\n    let attr = layoutAttributes.copy() as! UICollectionViewLayoutAttributes\r\n\r\n    self.setNeedsLayout()\r\n    self.layoutIfNeeded()\r\n        \r\n    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height\r\n    attr.frame.size.height = desiredHeight\r\n    self.frame = attr.frame  \/\/ Do NOT forget to set the frame or the layout won't get it !!!\r\n\r\n    return attr\r\n}<\/pre>\n<p>Let&#8217;s add a simpler version of it in the <code>ItemCell<\/code> class:<\/p>\n<pre class=\"lang:swift decode:true \" title=\"ItemCell - preferredLayoutAttributesFittingAttributes\" >override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -&gt; UICollectionViewLayoutAttributes {\r\n        \r\n    let attr = layoutAttributes.copy() as! UICollectionViewLayoutAttributes\r\n    self.setNeedsLayout()\r\n    self.layoutIfNeeded()\r\n    self.frame = attr.frame\r\n\r\n    return attr\r\n}<\/pre>\n<p>This part of the code enables the Layout to calculate the new size of the cell, matching its content.<\/p>\n<p>You don&#8217;t need similar code for <code>ImageCell<\/code> because its size is fixed, set in the custom layout and not re-calculated.<\/p>\n<p>And now, take a deep breath, we will create the long-awaited custom layout!<\/p>\n<h2>UICollectionViewFlowLayout<\/h2>\n<p>Let&#8217;s start with the first baby step:<\/p>\n<p>Create a new file, subclass of <code>UICollectionViewFlowLayout<\/code> and name it <code>MyCollectionViewFlowLayout<\/code>.<\/p>\n<p>Then, let&#8217;s add some configuration: section variables, initializations and basic setup.<\/p>\n<pre class=\"lang:swift decode:true \" title=\"UICollectionViewFlowLayout basic setup\" >let cellHeight: CGFloat = 200\r\nvar itemWidth: CGFloat = 200\r\nlet itemRatio: CGFloat = 1.33\r\nvar layoutInfo = [NSIndexPath: UICollectionViewLayoutAttributes]()\r\nvar layoutHeaderInfo = [NSIndexPath: UICollectionViewLayoutAttributes]()\r\nvar collectionViewWidth: CGFloat = 0\r\nvar maxOriginY: CGFloat = 0\r\nvar textHeight: CGFloat = 0.0\r\nvar itemsPerRow = 0\r\n\r\nlet numberOfSections = 4\r\nlet sectionImage = 0\r\nlet sectionText = 1\r\nlet sectionItem = 2\r\n\r\n\r\noverride init() {\r\n    super.init()\r\n    setup()\r\n}\r\n\r\nrequired init(coder aDecoder: NSCoder) {\r\n    super.init(coder: aDecoder)!\r\n    setup()\r\n}\r\n\r\nfunc setup() {\r\n    \/\/ setting up some inherited values\r\n    \r\n    minimumInteritemSpacing = 0\r\n    minimumLineSpacing = 0\r\n    scrollDirection = UICollectionViewScrollDirection.Horizontal\r\n    \r\n    collectionViewWidth = UIScreen.mainScreen().bounds.width\r\n    if let collectionView = collectionView {\r\n        collectionViewWidth = collectionView.frame.size.width\r\n    }\r\n    \r\n    itemWidth = getItemWidth()\r\n    headerReferenceSize = CGSizeMake(collectionViewWidth, 50)\r\n}\r\n\r\n\r\nfunc getItemWidth() -&gt; CGFloat {\r\n    \/\/ calculate here the size of the items -- see example on GitHub\r\n}\r\n<\/pre>\n<p>Also, don&#8217;t forget to add the class in the <code>Collection View attributes<\/code> in the Storyboard. Select &#8220;Custom&#8221; and type &#8220;CustomCollectionViewLayout&#8221; in the class field. Obvious, but oh so easy to forget ;)<\/p>\n<h2>PrepareLayout<\/h2>\n<p>Now, let&#8217;s tackle the heart of the <del>problem<\/del> custom layout:<\/p>\n<p>The first function to create is <code>prepareLayout<\/code>, which, as its name states, prepares the layout.<\/p>\n<p>The documentation says :<\/p>\n<div style=\"padding-left: 30px;\"><em><\/p>\n<p>&#8220;Tells the layout object to update the current layout.<br \/>\nLayout updates occur the first time the collection view presents its content and whenever the layout is invalidated explicitly or implicitly because of a change to the view. During each layout update, the collection view calls this method first to give your layout object a chance to prepare for the upcoming layout operation.<br \/>\nThe default implementation of this method does nothing. Subclasses can override it and use it to set up data structures or perform any initial computations needed to perform the layout later.&#8221;<\/p>\n<p><\/em><\/div>\n<p>In this function, we will loop over the sections and items to get the actual size of each element, and to keep track of the height of the content.<br \/>\nAnd as we would like to have a code as elegant as possible, we will do the size calculation in a separate function, called <code>frameForItemAtIndexPath<\/code>.<\/p>\n<pre class=\"lang:swift decode:true \" title=\"prepareLayout\" >override func prepareLayout() {\r\n    \r\n    maxOriginY = 0\r\n    setup() \/\/ can also be triggered only if the layout or collectionView bounds have changed\r\n    \r\n    for i in 0 ..&lt; numberOfSections {\r\n        \r\n        if let collectionView = self.collectionView {\r\n            \r\n            for j in 0 ..&lt; collectionView.numberOfItemsInSection(i) {\r\n                let indexPath = NSIndexPath(forRow: j, inSection: i)\r\n                \r\n                let itemAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)\r\n                itemAttributes.frame = frameForItemAtIndexPath(indexPath)\r\n                \r\n                if (indexPath.section &lt; sectionItem) {\r\n                    maxOriginY += itemAttributes.frame.size.height\r\n                    \r\n                } else if (indexPath.section == sectionItem) {\r\n                    let allRows = (indexPath.item % Int(itemsPerRow)) == (Int(itemsPerRow) - 1)\r\n                    let lastRow = indexPath.item == (collectionView.numberOfItemsInSection(i) - 1)\r\n                    \r\n                    if (allRows || lastRow) {\r\n                        \/\/ add height only if there is \"itemsPerRow\" items per row, or if multiple rows: last item per row\r\n                        maxOriginY += itemAttributes.frame.size.height\r\n                    }\r\n                }\r\n                layoutInfo[indexPath] = itemAttributes\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n\r\nfunc frameForItemAtIndexPath(indexPath: NSIndexPath) -&gt; CGRect {\r\n    \r\n    if (indexPath.section == sectionImage) {\r\n        return CGRectMake(0, maxOriginY, collectionViewWidth, collectionViewWidth)\r\n        \r\n    } else if (indexPath.section == sectionText) {\r\n        \r\n        var currentCellHeight = max(textHeight, cellHeight)\r\n        if let collectionView = self.collectionView {\r\n            if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? TextCell {\r\n                textHeight = max(textHeight, cell.frame.height)\r\n                currentCellHeight = textHeight\r\n            }\r\n        }\r\n        return CGRectMake(0, maxOriginY, collectionViewWidth, currentCellHeight) \/\/ width, height\r\n        \r\n    } else if (indexPath.section == sectionItem) {\r\n        \r\n        let currentColumn = ceil(CGFloat(indexPath.item % itemsPerRow))\r\n        let posX = currentColumn * itemWidth\r\n        let rect = CGRectMake(posX, maxOriginY, itemWidth, itemWidth * itemRatio)\r\n        \r\n        return rect\r\n    }\r\n    return CGRectZero\r\n}<\/pre>\n<p>Then, we will make sure that the <code>UICollectionView<\/code> has the right size:<\/p>\n<pre class=\"lang:swift decode:true \" title=\"collectionViewContentSize\" >override func collectionViewContentSize() -&gt; CGSize {\r\n    \r\n    guard let collectionView = self.collectionView else { return CGSizeMake(UIScreen.mainScreen().bounds.width, maxOriginY)}\r\n    return CGSizeMake(collectionView.frame.width, maxOriginY)\r\n}<\/pre>\n<p>Pretty straight forward.<\/p>\n<h2>Layout Attributes<\/h2>\n<p>Two more thing and we&#8217;re done! Now that we have calculated the size of the cells and that we know the size of the <code>UICollectionView<\/code>, we can determine the <code>attributes<\/code>, ie what will appear on screen in a <code>rect<\/code> or at a certain <code>indexPath<\/code>:<\/p>\n<pre class=\"lang:swift decode:true \" title=\"Layout attributes\" >override func layoutAttributesForElementsInRect(rect: CGRect) -&gt; [UICollectionViewLayoutAttributes]? {\r\n    \r\n    var allAttributes: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()\r\n\r\n    for (_, attributes) in layoutInfo {\r\n        if CGRectIntersectsRect(rect, attributes.frame) {\r\n            allAttributes.append(attributes)\r\n        }\r\n    }\r\n    return allAttributes\r\n}\r\n\r\noverride func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -&gt; UICollectionViewLayoutAttributes? {\r\n    return layoutInfo[indexPath]\r\n}<\/pre>\n<h2>Should invalidate layout<\/h2>\n<p>And the last step to finalize the layout: we want to invalidate the layout when needed, ie make sure the cells are calculated according to the actual space available, and not according to the layout estimated size.<\/p>\n<pre class=\"lang:swift decode:true \" title=\"Invalidate layout\" >override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -&gt; Bool {\r\n    \/\/ if you don't want to re-calculate everything on scroll\r\n    return !CGSizeEqualToSize(newBounds.size, self.collectionView!.frame.size)\r\n}\r\n\r\n\r\noverride func shouldInvalidateLayoutForPreferredLayoutAttributes(preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -&gt; Bool {\r\n    \r\n    if (originalAttributes.indexPath.section &gt;= sectionText) {\r\n        \/\/ true only for sections that needs to be updated (the ones that are changing size)\r\n        \/\/ ie. the ones that are using preferredLayoutAttributesFittingAttributes to self-resize\r\n        return true\r\n    }\r\n    return false\r\n}<\/pre>\n<p>I hope this tutorial series helped a bit. As I said at the beginning, I tried to cover different cell configurations to give you the tools to start experimenting with custom layouts, now it&#8217;s up to you to go further! And, who knows, maybe Apple will soon release something to help us deal (easily) with variable height in <code>UICollectionViewCells<\/code> like they did for <code>UITableView<\/code>&#8230; <\/p>\n<p>All the code for this tutorial is available on GitHub: <a href=\"https:\/\/github.com\/strawberrycode\/SCCollectionViewCustomLayout\" target=\"_blank\">SCCollectionViewCustomLayout<\/a>. As usual, don&#8217;t hesitate to fork it and play with it! Also, there is a little bonus: I&#8217;ve included a dynamic <code>HeaderView<\/code> in the <code>UICollectionView<\/code> and Custom Layout :) You&#8217;re welcome! <\/p>\n<div style=\"margin-top:50px\">\nIf you have read that far, thank you! Really! I know it has been long, but you won&#8217;t go unrewarded, I have some extra tips to share with you :)\n<\/div>\n<h3>Pro tips: <\/h3>\n<ul>\n<li>Try it on the iPad, it&#8217;s responsive! (if you add <code>viewWillLayoutSubviews()<\/code> to the <code>UIViewController<\/code>, like in the example on GitHub)<\/li>\n<li>If you are caught in an infinite loop in the <code>UICollectionViewFlowLayout<\/code> that makes the app crash, double check the size (and potential margins) of the cells you are trying to render. If it doesn&#8217;t match with the available space, it will try to calculate it again until it fits, which isn&#8217;t likely to happen&#8230; and it will eventually crash.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Welcome back to the &#8220;UICollectionView Custom Layouts: the Ultimate Tutorial&#8221; series. This is Part 2, where we will actually tackle the custom layout part. If you haven&#8217;t seen or done the first part, I strongly recommend you to take a look at it, as we will be using the UICollectionView we created there. Remember what we had at the end of Part 1, when we run the project without the custom layout, and what we would like to achieve? &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A few words A few words before we start: I recommend you to have a look at Session 232 of &hellip; <a href=\"https:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">UICollectionView Custom Layout: the Ultimate Tutorial \u2013 Part 2<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":459,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[40],"tags":[17,82,41,69,85],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v16.1.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>UICollectionView Custom Layout: the Ultimate Tutorial \u2013 Part 2 - StrawberryCode<\/title>\n<meta name=\"description\" content=\"If you want to know how to create a custom layout for UICollectionView, this article is for you! This is Part 2 of UICollectionView Custom Layout tutorial.\" \/>\n<link rel=\"canonical\" href=\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"UICollectionView Custom Layout: the Ultimate Tutorial \u2013 Part 2 - StrawberryCode\" \/>\n<meta property=\"og:description\" content=\"If you want to know how to create a custom layout for UICollectionView, this article is for you! This is Part 2 of UICollectionView Custom Layout tutorial.\" \/>\n<meta property=\"og:url\" content=\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"StrawberryCode\" \/>\n<meta property=\"article:published_time\" content=\"2016-05-29T02:22:39+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2016-05-30T20:08:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/strawberrycode.com\/blog\/wp-content\/uploads\/2016\/05\/Layout.png\" \/>\n\t<meta property=\"og:image:width\" content=\"200\" \/>\n\t<meta property=\"og:image:height\" content=\"200\" \/>\n<meta name=\"twitter:label1\" content=\"Estimated reading time\">\n\t<meta name=\"twitter:data1\" content=\"6 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/strawberrycode.com\/blog\/#website\",\"url\":\"https:\/\/strawberrycode.com\/blog\/\",\"name\":\"StrawberryCode\",\"description\":\"Fruit for Thought\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/strawberrycode.com\/blog\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-GB\"},{\"@type\":\"ImageObject\",\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/#primaryimage\",\"inLanguage\":\"en-GB\",\"url\":\"https:\/\/strawberrycode.com\/blog\/wp-content\/uploads\/2016\/05\/Layout.png\",\"contentUrl\":\"https:\/\/strawberrycode.com\/blog\/wp-content\/uploads\/2016\/05\/Layout.png\",\"width\":200,\"height\":200,\"caption\":\"UICollectionView Custom Layouts\"},{\"@type\":\"WebPage\",\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/#webpage\",\"url\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\",\"name\":\"UICollectionView Custom Layout: the Ultimate Tutorial \\u2013 Part 2 - StrawberryCode\",\"isPartOf\":{\"@id\":\"https:\/\/strawberrycode.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/#primaryimage\"},\"datePublished\":\"2016-05-29T02:22:39+00:00\",\"dateModified\":\"2016-05-30T20:08:52+00:00\",\"author\":{\"@id\":\"https:\/\/strawberrycode.com\/blog\/#\/schema\/person\/c328d959959928f47281d7a0ec779e2a\"},\"description\":\"If you want to know how to create a custom layout for UICollectionView, this article is for you! This is Part 2 of UICollectionView Custom Layout tutorial.\",\"breadcrumb\":{\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/strawberrycode.com\/blog\/\",\"url\":\"https:\/\/strawberrycode.com\/blog\/\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"@type\":\"WebPage\",\"@id\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\",\"url\":\"http:\/\/strawberrycode.com\/blog\/uicollectionview-custom-layout-the-ultimate-tutorial-part-2\/\",\"name\":\"UICollectionView Custom Layout: the Ultimate Tutorial \\u2013 Part 2\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/strawberrycode.com\/blog\/#\/schema\/person\/c328d959959928f47281d7a0ec779e2a\",\"name\":\"StrawberryCode\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/posts\/374"}],"collection":[{"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/comments?post=374"}],"version-history":[{"count":33,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/posts\/374\/revisions"}],"predecessor-version":[{"id":490,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/posts\/374\/revisions\/490"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/media\/459"}],"wp:attachment":[{"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/media?parent=374"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/categories?post=374"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/strawberrycode.com\/blog\/wp-json\/wp\/v2\/tags?post=374"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}