-
-
Save alemar11/7886b0f3f298e628e67a009487ae7c01 to your computer and use it in GitHub Desktop.
Instagram Stories in SwiftUI. Demo video: https://twitter.com/fabiogiolito/status/1142924688525529088
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// InstaStories.swift | |
// SwiftUITests | |
// | |
// Created by Fabio Giolito on 23/06/2019. | |
// Copyright © 2019 Fabio Giolito. All rights reserved. | |
// | |
import Combine | |
import SwiftUI | |
struct Story: Identifiable { | |
var id: Int | |
var user: String | |
var videos: [Int] | |
} | |
struct InstaStories : View { | |
var stories = [ | |
Story(id: 0, user: "cat", videos: [0, 1, 2]), | |
Story(id: 1, user: "hand", videos: [0, 1, 2, 3]), | |
Story(id: 2, user: "man", videos: [0, 1]) | |
] | |
@State var currentStory: Int = 0 | |
@State var currentVideo: Int = 0 | |
var body: some View { | |
ZStack { | |
ForEach(stories.reversed()) { story in | |
ZStack { | |
ForEach(story.videos.identified(by: \.self)) { video in | |
Image("Story\(story.id)Video\(video)").resizable() | |
.opacity(self.isVisible(story: story.id, video: video) ? 1 : 0) | |
} | |
// Overlay UI | |
VStack { | |
HStack { | |
ForEach(story.videos.identified(by: \.self)) { video in | |
ZStack(alignment: .leading) { | |
if (self.isVisible(story: story.id, video: video)) { | |
Rectangle() | |
} | |
Rectangle().opacity(0.3) | |
}.frame(height: 4).cornerRadius(2) | |
} | |
} | |
AvatarBar(userAvatar: story.user, userName: story.user) | |
Spacer() | |
CommentsBar() | |
} | |
.foregroundColor(.white) | |
.padding() | |
}.offset(x: CGFloat(story.id - self.currentStory) * UIScreen.main.bounds.width) | |
.animation(.fluidSpring()) | |
} | |
// Navigation buttons | |
HStack { | |
Rectangle().opacity(0).tapAction { self.prevVideo() } | |
Rectangle().opacity(0).tapAction { self.nextVideo() } | |
} | |
}.cornerRadius(20).clipped() | |
} | |
func isVisible(story: Int, video: Int) -> Bool { | |
if (video == 0) { return true } | |
if (story < currentStory || (story == currentStory && video <= currentVideo)) { return true } | |
return false | |
} | |
func prevVideo() { | |
if (currentVideo > 0) { | |
currentVideo -= 1 | |
} else if (currentStory > 0) { | |
currentStory -= 1 | |
currentVideo = stories[currentStory].videos.count - 1 | |
print("prev story") | |
} else { | |
print("at lower limit") | |
} | |
} | |
func nextVideo() { | |
if (currentVideo < stories[currentStory].videos.count - 1) { | |
currentVideo += 1 | |
} else if (currentStory < stories.count - 1) { | |
currentStory += 1 | |
currentVideo = 0 | |
print("next story") | |
} else { | |
print("at top limit") | |
} | |
} | |
} | |
struct AvatarBar : View { | |
var userAvatar: String | |
var userName: String | |
var body: some View { | |
HStack { | |
Image(userAvatar).resizable().frame(width: 40, height: 40) | |
Text(userName) | |
Spacer() | |
Image(systemName: "xmark") | |
} | |
} | |
} | |
struct CommentsBar : View { | |
var body: some View { | |
HStack { | |
HStack { | |
Image(systemName: "camera.fill") | |
} | |
.frame(width: 40, height: 40) | |
.border(Color.white, width: 1, cornerRadius: 20) | |
HStack { | |
Text("Send message") | |
Spacer() | |
Image(systemName: "ellipsis") | |
} | |
.padding(.horizontal) | |
.frame(height: 40) | |
.border(Color.white, width: 1, cornerRadius: 20) | |
Image(systemName: "paperplane").imageScale(.large) | |
} | |
} | |
} | |
#if DEBUG | |
struct InstaStories_Previews : PreviewProvider { | |
static var previews: some View { | |
InstaStories() | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment