An experimental clone of the new iOS 11 App Store app

Overview

appstore-clone

An experimental clone of the new iOS 11 App Store app for this Medium Article

Reveal debugging my App Store clone

Description

Apple announced an entirely redesigned iOS App Store experience at WWDC 2017. It placed an increased focus on rich, in-depth, long form content over the previous interface. I wrote about the significance of the redesign, and my first impressions.

I decided to spend some spare time diving a little deeper into the User Interface of the new App Store itself in order to gain a deeper understanding of how it works and in some ways how I might put my own subtle, unique improvements on it if I’d had the chance.

What was initially just a late Friday night, nothing-better-to-do project recreating the Today view cards, ended up snowballing into an incredibly fun weekend-long passion project that I really enjoyed spending time on.

Features

Collection View

Today View is actually quite a simple interface, it’s obviously just a UICollectionView, and each card is a UICollectionViewCell. In order to duplicate these cards, I created a base UICollectionViewCell subclass called BaseRoundedCardCell that all card cells inherit to gain all of the underlying features that each card has, including the shadow.

Shadow

Each Card in the App Store app has a soft shadow, with a slight vertical offset which gives it a sense of depth and suggests to the user that they can tap on a card, to open its detailed story view. In the current App Store app this shadow seems to be just a static shadow. I decided not just to clone this shadow, but go even further using Core Motion to move the shadow based on the Pitch (Horizontal tilt) and Roll (Vertical tilt) of the device. In a subtle way this would make the interface feel more lively and rich, similar to the way that it does on the tvOS interface.

// Roll/Pitch Dynamic Shadow
if motionManager.isDeviceMotionAvailable {
    motionManager.deviceMotionUpdateInterval = 0.02
    motionManager.startDeviceMotionUpdates(to: .main, withHandler: { (motion, error) in
        if let motion = motion {
            let pitch = motion.attitude.pitch * 10 // x-axis
            let roll = motion.attitude.roll * 10 // y-axis
            self.applyShadow(width: CGFloat(roll), height: CGFloat(pitch))
        }
    })
}

Long Press Gesture

On the current App Store app each Card view itself is also fixed and not overly interactive. You can tap on a card to transition into the Story detail view but you can’t interact with a card in any other way. Just like I can with the tvOS interface, as a user I’m compelled to touch and manipulate cards a bit more than just a tap. I went ahead and improved on this by implementing a long press gesture that shrinks the card slightly when it is held down. This extends the depth metaphor that the shadow creates but is also not too excessive so as to make cards feel too flexible and unrealistic.

// MARK: - Gesture Recognizer
private func configureGestureRecognizer() {
    // Long Press Gesture Recognizer
    longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(gestureRecognizer:)))
    longPressGestureRecognizer?.minimumPressDuration = 0.1
    addGestureRecognizer(longPressGestureRecognizer!)
}

internal func handleLongPressGesture(gestureRecognizer: UILongPressGestureRecognizer) {
    if gestureRecognizer.state == .began {
        handleLongPressBegan()
    } else if gestureRecognizer.state == .ended || gestureRecognizer.state == .cancelled {
        handleLongPressEnded()
    }
}

private func handleLongPressBegan() {
    guard !isPressed else {
        return
    }

    isPressed = true
    UIView.animate(withDuration: 0.5,
                   delay: 0.0,
                   usingSpringWithDamping: 0.8,
                   initialSpringVelocity: 0.2,
                   options: .beginFromCurrentState,
                   animations: {
                       self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
                   }, completion: nil)
}

private func handleLongPressEnded() {
    guard isPressed else {
        return
    }

    UIView.animate(withDuration: 0.5,
                   delay: 0.0,
                   usingSpringWithDamping: 0.4,
                   initialSpringVelocity: 0.2,
                   options: .beginFromCurrentState,
                   animations: {
                       self.transform = CGAffineTransform.identity
                   }) { (finished) in
        self.isPressed = false
    }
}

iPad Grid Layout

iPad Grid Layout

Depending on if you’re viewing the new App Store app on an iPad or an iPhone, the cards will layout differently. On an iPhone you’ll see one vertical column of cards all of the exact same width and height, while on an iPad you’ll see two columns of cells and each cell will alternate between compressed width and expanded width, to provide more of a grid layout that makes better of use of the iPads larger screen real estate.

// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    if UIDevice.current.userInterfaceIdiom == .phone {
        return CGSize(width: collectionView.bounds.width, height: BaseRoundedCardCell.cellHeight)
    } else {
        
        // Number of Items per Row
        let numberOfItemsInRow = 2
        
        // Current Row Number
        let rowNumber = indexPath.item/numberOfItemsInRow
        
        // Compressed With
        let compressedWidth = collectionView.bounds.width/3
        
        // Expanded Width
        let expandedWidth = (collectionView.bounds.width/3) * 2
        
        // Is Even Row
        let isEvenRow = rowNumber % 2 == 0
        
        // Is First Item in Row
        let isFirstItem = indexPath.item % numberOfItemsInRow != 0
        
        // Calculate Width
        var width: CGFloat = 0.0
        if isEvenRow {
            width = isFirstItem ? compressedWidth : expandedWidth
        } else {
            width = isFirstItem ? expandedWidth : compressedWidth
        }
        
        return CGSize(width: width, height: BaseRoundedCardCell.cellHeight)
    }
}

Section Headers

Since the Today view is a timeline style interface, cards need to be separated out into daily sections and marked by their dates. This allows the user to scroll down, through cards and catch up on the editorial content from previous days that they may have missed. While using the new App Store app I noticed that these headers are not sticky, but infact scroll along with the rest of the UICollectionViewCells, therefore they must be UICollectionReusableView’s used as Section Headers for representing each day.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    return CGSize(width: collectionView.bounds.width, height: TodaySectionHeader.viewHeight)
}

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    let sectionHeader = TodaySectionHeader.dequeue(fromCollectionView: collectionView, ofKind: kind, atIndexPath: indexPath)
    sectionHeader.shouldShowProfileImageView = (indexPath.section == 0)
    return sectionHeader
}

Contributions

I have no immediate plans to actively work on this experiment any further. However this source code is licensed under the MIT license which permits anyone to fork this repository and make modifications under the same license.

You might also like...
📱The official Wikipedia iOS app.

Wikipedia iOS The official Wikipedia iOS app. License: MIT License Source repo: https://github.com/wikimedia/wikipedia-ios Planning (bugs & features):

Sample iOS app demonstrating Coordinators, Dependency Injection, MVVM, Binding

iOS Sample App Sample iOS app written the way I write iOS apps because I cannot share the app I currently work on. Shown concepts Architecture concept

An iOS widget-based HN reader
An iOS widget-based HN reader

Benuse, an iOS widget-based HN reader Why did you build this app? There already exist some great native Hacker News clients for iOS. I would recommend

WordPress for iOS - Official repository

WordPress for iOS Build Instructions Please refer to the sections below for more detailed information. The instructions assume the work is performed f

Fully open source text editor for iOS written in Swift.
Fully open source text editor for iOS written in Swift.

Edhita Fully open source text editor for iOS written in Swift. http://edhita.bornneet.com/ What Edhita means? Edhita (Romaji) == エディタ (Katakana) == Ed

iOS Money Manager for Board Games
iOS Money Manager for Board Games

Warning! This is an old project. It does contain a lot of bad code I wrote when I started out with iOS Development. I am not mantaining this project a

Turning on a VPN is always a painful experience on an iOS device due to the deep nested menus.
Turning on a VPN is always a painful experience on an iOS device due to the deep nested menus.

VPN On Turning on a VPN is always a painful experience on an iOS device due to the deep nested menus. This App installs a Today Widget into Notificati

Discover, download, compile & launch different image processing & style transfer CoreML models on iOS.
Discover, download, compile & launch different image processing & style transfer CoreML models on iOS.

⚠️ ⚠️ ⚠️ IMPORTANT: I'm no longer maintaining Awesome-ML. Awesome ML is an iOS app that is made to demonstrate different image processing CoreML model

Kommunicate iOS SDK for customer support
Kommunicate iOS SDK for customer support

Kommunicate iOS Chat SDK for Customer Support An Open Source iOS Live Chat SDK for Customer Support Overview Kommunicate provides open source live cha

Comments
  • Shadow jumps left and right

    Shadow jumps left and right

    I was testing it on my iPhone and when I hold the device vertically and tilt the top of the phone slightly forward then tilt the phone slightly left and right the shadow suddenly jumps left and right. I tried to do the same with the app store and that doesnt happen. recording

    opened by nasserprofessional 0
  • Scrolling with Long Press

    Scrolling with Long Press

    Why i can't scroll with long press? when i use handleLongPressEnded() in gesture.state == .changed then handleLongPressEnded() gets called but collection view did not scroll

    opened by Awais1234567 0
  • Master

    Master

    Edited fonts and layout. Fixed transition to and from position. Added custom for when lineheight is smaller than fontsize and designables for kerning and lineheight.

    opened by YannisDC 0
  • More animation to do

    More animation to do

    Hi there,

    I just came across this repo - good job!

    However, it seems some animations is still missing: like when click one app view, the animation has a bounce effect. Any ideas?

    Also, the pan gesture, there is a down-pan gesture state not implemented yet

    opened by liuxuan30 1
Owner
Phill Farrugia
Takes photos, writes words and codes things.
Phill Farrugia
Firefox for iOS, branch works with Xcode 12.5.1, Swift 5.4.2 and supports iOS 11.4 and above.

Firefox for iOS Download on the App Store. This branch (main) This branch works with Xcode 12.5.1, Swift 5.4.2 and supports iOS 11.4 and above. Please

Mozilla Mobile 11.2k Jan 7, 2023
Sample app to demonstrate data sharing between a WatchKit app and its main app using Realm

#Done! A sample app demonstrating how to share data between an app an its Watch extension using Realm. You can read more about it here. ##Screenshot #

Fancy Pixel 147 Dec 8, 2022
iOS app to record how much things cost using various data persistence implementations.

how-much iOS app to record how much things cost using various data persistence implementations. The basic data unit is an item, a simple dictionary: {

null 22 Aug 15, 2022
The (second) best iOS app for GitHub.

GitHawk is the second-highest rated iOS app for GitHub. Features 0️⃣ Inbox Zero your notifications ?? Comment even faster than on GitHub desktop ?? Th

GitHawk 2.8k Dec 23, 2022
iOS app for 5calls.org

5Calls iOS App This is the repository for the iOS app for 5Calls.org. Requirements Xcode 10.2.1 iOS 10.2 Getting Started Install the dependencies: bun

5 Calls 129 Dec 25, 2022
Development of the TUM Campus App for iOS devices - for and from students at Technical University of Munich.

Tum Campus App - An Unofficial Guide Through University Life The TUM Campus App (TCA) is an open source project, developed by volunteers and available

TUM Developers 93 Dec 16, 2022
Lightweight iOS Photo Blur App

Blurry Blurry is the go-to image blurring tool to help you apply beautiful blurs for your photos. It is perfect for creating wallpapers, backgrounds,

Andy 17 Nov 22, 2022
Alfresco iOS App - Alfresco is the open platform for business-critical content management and collaboration.

Welcome to the Alfresco iOS App Alfresco is the open platform for business-critical content management and collaboration. Alfresco Mobile was designed

Alfresco Software 42 Sep 26, 2022
📱 Nextcloud iOS app

Nextcloud iOS app Check out https://nextcloud.com and follow us on twitter.com/nextclouders or twitter.com/NextcloudiOS How to contribute If you want

Nextcloud 1.4k Dec 30, 2022
🍣Making Recipes iOS app

Recipes App ❤️ Support my apps ❤️ Push Hero - pure Swift native macOS application to test push notifications PastePal - Pasteboard, note and shortcut

Khoa 88 Nov 22, 2022