Last active
September 13, 2022 14:20
-
-
Save fabiogiolito/67c0fe45972685b5101594e75bb7f35b to your computer and use it in GitHub Desktop.
Instagram Stories in SwiftUI. Demo video: https://twitter.com/fabiogiolito/status/1142924688525529088
This file contains 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 |
Yeah I'm still working on how to describe where to put stuff in SwiftUI. I should have said added to the last child of body, that would have been more clear. And yeah those updates sound good so you have more control over the timer. Then you could put a button or something to toggle the timer on/off too. Good work!
Also check you Facebook reactions in SwiftUI: https://gist.github.com/fabiogiolito/424e1d68766fc054ad4bf6fd3506e9c3
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@davidahouse Thank you. Can't use it on body, but works as expected when added to the first child.
Still some ways to go, I need to move it to a variable so I can restart it when user taps to move forward/backwards. And call
.fire()
from that.onAppear
method. I'll get back to this later and update the gist.Thanks again!