Last active
March 21, 2020 18:05
-
-
Save BestKora/2b28953f5f1a52a18126af5c0ae4ade5 to your computer and use it in GitHub Desktop.
Control vertical move of the ScrollView in SwiftUI.
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
import SwiftUI | |
import Combine | |
public struct BottomSheet<Content>: View where Content : View { | |
/// The content of the scroll view. | |
public var content: Content | |
/// The offset of the scrollview updated as the scroll view scrolls | |
@Binding public var offset : CGSize | |
@State private var offsetContent: CGPoint = .zero | |
@State private var accumulated = CGSize.zero | |
@State private var moveScroll = true | |
private var currentOffsetPoint: AnyPublisher<CGPoint, Never> { | |
Just(self.offsetContent) | |
.receive(on: DispatchQueue.main) | |
.eraseToAnyPublisher() | |
} | |
public init(offset: Binding<CGSize> = .constant(.zero), @ViewBuilder content: () -> Content) { | |
self._offset = offset | |
self.content = content() | |
} | |
// Declares the content and behavior of this view. | |
public var body: some View { | |
GeometryReader { geometry in | |
VStack { | |
if self.moveScroll { | |
OffsetScrollView(offset: self.$offsetContent) { | |
self.content | |
} /// scroll | |
.offset(self.offset) | |
.gesture( DragGesture(minimumDistance: 0) | |
.onChanged ({value in | |
if self.accumulated == CGSize.zero { | |
self.accumulated = self.offset | |
} | |
let localOffset = value.translation.height + self.accumulated.height | |
self.offset = CGSize(width: 0, | |
height: min(max(localOffset,10),(geometry.size.height - 20))) | |
if self.offset.height <= 10 {self.moveScroll = false} | |
}) | |
.onEnded ({ value in | |
let localOffset = value.translation.height + self.accumulated.height | |
self.accumulated = CGSize(width: 0, | |
height: min(max(localOffset,10),(geometry.size.height - 20))) | |
} | |
)) /// gesture | |
//----------------- | |
} else { | |
OffsetScrollView(offset: self.$offsetContent) { | |
self.content | |
} /// scroll | |
.offset(self.offset) | |
.onReceive(self.currentOffsetPoint) { point in | |
if !self.moveScroll { | |
if point.y >= 10 { | |
self.moveScroll = true | |
self.accumulated = CGSize(width: 0, height: point.y) | |
self.offset = CGSize(width: 0, height: point.y) | |
} | |
} | |
} /// on receive | |
} // else | |
} /// vstack | |
} // geometry | |
} | |
} |
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
import SwiftUI | |
struct ContentView: View { | |
@State var restaurant = restaurants[0] | |
var body: some View { | |
ZStack { | |
VStack { | |
List { | |
ForEach(restaurants) { restaurant in | |
BasicImageRow(restaurant: restaurant) | |
.onTapGesture { | |
self.restaurant = restaurant | |
} | |
.background(self.restaurant == restaurant ? Color.gray .opacity(0.3) : Color.white) | |
} | |
} | |
} | |
RestaurantDetailView1(offset: CGSize(width: 0, height: 500), restaurant: restaurant) | |
} | |
} | |
} | |
struct BasicImageRow: View { | |
var restaurant: Restaurant | |
var body: some View { | |
HStack { | |
Image(restaurant.image) | |
.resizable() | |
.frame(width: 40, height: 40) | |
.cornerRadius(5) | |
Text(restaurant.name) | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
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
// Created by Zac White on 9/30/19. | |
// Copyright © 2019 Zac White. All rights reserved. | |
//https://zacwhite.com/2019/scrollview-content-offsets-swiftui/ | |
import SwiftUI | |
public struct OffsetScrollView<Content>: View where Content : View { | |
/// The content of the scroll view. | |
public var content: Content | |
/// The scrollable axes. | |
/// | |
/// The default is `.vertical`. | |
public var axes: Axis.Set | |
/// If true, the scroll view may indicate the scrollable component of | |
/// the content offset, in a way suitable for the platform. | |
/// | |
/// The default is `true`. | |
public var showsIndicators: Bool | |
/// The initial offset in the global frame, used for calculating the relative offset | |
@State private var initialOffset: CGPoint? = nil | |
/// The offset of the scrollview updated as the scroll view scrolls | |
@Binding public var offset: CGPoint | |
public init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, offset: Binding<CGPoint> = .constant(.zero), @ViewBuilder content: () -> Content) { | |
self.axes = axes | |
self.showsIndicators = showsIndicators | |
self._offset = offset | |
self.content = content() | |
} | |
/// observe offset | |
func makeView(geometry: GeometryProxy) -> some View { | |
DispatchQueue.main.async { | |
let globalOrigin = geometry.frame(in: .global).origin | |
self.initialOffset = self.initialOffset ?? globalOrigin | |
let initialOffset = (self.initialOffset ?? .zero) | |
let offset = CGPoint(x: globalOrigin.x - initialOffset.x, | |
y: globalOrigin.y - initialOffset.y) | |
self.offset = offset | |
} | |
return AnyView(EmptyView()) | |
} | |
/// Declares the content and behavior of this view. | |
public var body: some View { | |
ScrollView(axes, showsIndicators: showsIndicators) { | |
VStack(alignment: .leading, spacing: 0) { | |
GeometryReader { geometry in | |
self.makeView(geometry: geometry) | |
}.frame(width: 0, height: 0) | |
content | |
} | |
} | |
} | |
} |
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
import Foundation | |
struct Restaurant: Identifiable { | |
var id: UUID = UUID() | |
var name: String | |
var type: String | |
var location: String | |
var phone: String | |
var description: String | |
var image: String | |
var isVisited: Bool | |
init(name: String, type: String, location: String, phone: String, description: String, image: String, isVisited: Bool) { | |
self.name = name | |
self.type = type | |
self.location = location | |
self.phone = phone | |
self.description = description | |
self.image = image | |
self.isVisited = isVisited | |
} | |
init() { | |
self.init(name: "", type: "", location: "", phone: "", description: "", image: "", isVisited: false) | |
} | |
} | |
extension Restaurant{ | |
static func == (lhs: Restaurant, rhs: Restaurant) -> Bool{ | |
return lhs.id == rhs.id | |
} | |
} | |
#if DEBUG | |
var restaurants: [Restaurant] = [ | |
Restaurant(name: "Cafe Deadend", type: "Coffee & Tea Shop", location: "G/F, 72 Po Hing Fong, Sheung Wan, Hong Kong", phone: "232-923423", description: "Searching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.\n\nA little gem hidden at the corner of the street is nothing but fantastic! This place is warm and cozy. We open at 7 every morning except Sunday, and close at 9 PM. We offer a variety of coffee drinks and specialties including lattes, cappuccinos, teas, and more. We serve breakfast, lunch, and dinner in an airy open setting. Come over, have a coffee and enjoy a chit-chat with our baristas.\n\nA great cafe in Austrian style. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. We also serve breakfast and light lunch. Come over to enjoy the elegant ambience and quality coffee.", image: "cafedeadend", isVisited: false), | |
Restaurant(name: "Homei", type: "Cafe", location: "Shop B, G/F, 22-24A Tai Ping San Street SOHO, Sheung Wan, Hong Kong", phone: "348-233423", description: "A little gem hidden at the corner of the street is nothing but fantastic! This place is warm and cozy. We open at 7 every morning except Sunday, and close at 9 PM. We offer a variety of coffee drinks and specialties including lattes, cappuccinos, teas, and more. We serve breakfast, lunch, and dinner in an airy open setting. Come over, have a coffee and enjoy a chit-chat with our baristas.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "homei", isVisited: false), | |
Restaurant(name: "Teakha", type: "Tea House", location: "Shop B, 18 Tai Ping Shan Road SOHO, Sheung Wan, Hong Kong", phone: "354-243523", description: "Take a moment to stop and smell tealeaves! We are about the community, our environment, and all things created by the warmth of our hands. We open at 11 AM, and close at 7 PM. At teakha, we sell only the best single-origin teas sourced by our sister company Plantation directly from small tea plantations. The teas are then either cooked to perfection with milk in a pot or brewed.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "teakha", isVisited: false), | |
Restaurant(name: "Cafe loisl", type: "Austrian / Causual Drink", location: "Shop B, 20 Tai Ping Shan Road SOHO, Sheung Wan, Hong Kong", phone: "453-333423", description: "A great cafe in Austrian style. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. We also serve breakfast and light lunch. Come over to enjoy the elegant ambience and quality coffee.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "cafeloisl", isVisited: false), | |
Restaurant(name: "Petite Oyster", type: "French", location: "24 Tai Ping Shan Road SOHO, Sheung Wan, Hong Kong", phone: "983-284334", description: "An upscale dining venue, features premium and seasonal imported oysters, and delicate yet creative modern European cuisines. Its oyster bar displays a full array of freshest oysters imported from all over the world including France, Australia, USA and Japan.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "petiteoyster", isVisited: false), | |
Restaurant(name: "For Kee Restaurant", type: "Hong Kong", location: "Shop J-K., 200 Hollywood Road, SOHO, Sheung Wan, Hong Kong", phone: "232-434222", description: "A great local cafe for breakfast and lunch! Located in a quiet area in Sheung Wan, we offer pork chop buns, HK french toast, and many more. We open from 7 AM to 4:30 PM.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "forkeerestaurant", isVisited: false), | |
Restaurant(name: "Po's Atelier", type: "Bakery", location: "G/F, 62 Po Hing Fong, Sheung Wan, Hong Kong", phone: "234-834322", description: "A boutique bakery focusing on artisan breads and pastries paired with inspiration from Japan and Scandinavia. We are crazy about bread and excited to share our artisan bakes with you. We open at 10 every morning, and close at 7 PM.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "posatelier", isVisited: false), | |
Restaurant(name: "Bourke Street Backery", type: "Chocolate", location: "633 Bourke St Sydney New South Wales 2010 Surry Hills", phone: "982-434343", description: "We make everything by hand with the best possible ingredients, from organic sourdoughs to pastries and cakes. A combination of great produce, good strong coffee, artisanal skill and hard work creates the honest, soulful, delectable bites that have made Bourke Street Bakery famous. Visit us at one of our many Sydney locations!\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "bourkestreetbakery", isVisited: false), | |
Restaurant(name: "Haigh's Chocolate", type: "Cafe", location: "412-414 George St Sydney New South Wales", phone: "734-232323", description: "Haigh's Chocolates is Australia's oldest family owned chocolate maker. We have been making chocolate in Adelaide, South Australia since 1915 and we are committed to the art of premium chocolate making from the cocoa bean. Our chocolates are always presented to perfection in our own retail stores. Visit any one of them and you'll find chocolate tasting, gift wrapping and personalised attention are all part of the service.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "haighschocolate", isVisited: false), | |
Restaurant(name: "Palomino Espresso", type: "American / Seafood", location: "Shop 1 61 York St Sydney New South Wales", phone: "872-734343", description: "We offer an assortment of on-site baked goods and sandwiches. This place has always been a favourite among office workers. We open at 7 every morning including Sunday, and close at 4 PM. Come over, have a coffee and enjoy a chit-chat with our baristas.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "palominoespresso", isVisited: false), | |
Restaurant(name: "Upstate", type: "Seafood", location: "95 1st Ave New York, NY 10003", phone: "343-233221", description: "The absolute best seafood place in town. The atmosphere here creates a very homey feeling. We open at 5 PM, and close at 10:30 PM. ", image: "upstate", isVisited: false), | |
Restaurant(name: "Traif", type: "American", location: "229 S 4th St Brooklyn, NY 11211", phone: "985-723623", description: "A young crowd populates this pork-focused American eatery in an older Williamsburg neighborhood. We open at 6PM, and close at 11 PM. If you enjoy a shared small plates dinner experience, come over and have a try.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "traif", isVisited: false), | |
Restaurant(name: "Graham Avenue Meats", type: "Breakfast & Brunch", location: "445 Graham Ave Brooklyn, NY 11211", phone: "455-232345", description: "Classic Italian deli & butcher draws patrons with meat-filled submarine sandwiches. We use the freshest meats and veggies to create the perfect panini for you. We look forward to seeing you!\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "grahamavenuemeats", isVisited: false), | |
Restaurant(name: "Waffle & Wolf", type: "Coffee & Tea", location: "413 Graham Ave Brooklyn, NY 11211", phone: "434-232322", description: "Small shop, some seating, definitely something different! We open at 7 every morning except Sunday, and close at 9 PM. We offer a variety of waffles with choices of sweet & savory fillings. If you are gluten free and craving waffles, this is the place to go.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "wafflewolf", isVisited: false), | |
Restaurant(name: "Five Leaves", type: "Bistro", location: "18 Bedford Ave Brooklyn, NY 11222", phone: "343-234553", description: " Great food, cocktails, ambiance, service. Nothing beats having a warm dinner and a whiskey. We open at 8 every morning, and close at 1 AM. The ricotta pancakes are something you must try.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "fiveleaves", isVisited: false), | |
Restaurant(name: "Cafe Lore", type: "Latin American", location: "Sunset Park 4601 4th Ave Brooklyn, NY 11220", phone: "342-455433", description: "Good place, great environment and amazing food! We open at 10 every morning except Sunday, and close at 9 PM. Give us a visit! Enjoy mushroom raviolis, pumpkin raviolis, meat raviolis with sausage and peppers, pork cutlets, eggplant parmesan, and salad.", image: "cafelore", isVisited: false), | |
Restaurant(name: "Confessional", type: "Spanish", location: "308 E 6th St New York, NY 10003", phone: "643-332323", description: "Most delicious cocktail you would ever try! Our menu includes a wide range of high quality internationally inspired dishes, vegetarian options, and local cuisine. We open at 10 AM, and close at 10 PM. We welcome you into our place to enjoy a meal with your friends.", image: "confessional", isVisited: false), | |
Restaurant(name: "Barrafina", type: "Spanish", location: "54 Frith Street London W1D 4SL United Kingdom", phone: "542-343434", description: "a collection of authentic Spanish Tapas bars in Central London! We have an open kitchen, a beautiful marble-topped bar where guests can sit and watch the chefs at work and stylish red leather stools. Lunch starts at 1 PM. Dinners starts 5:30 PM.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "barrafina", isVisited: false), | |
Restaurant(name: "Donostia", type: "Spanish", location: "10 Seymour Place London W1H 7ND United Kingdom", phone: "722-232323", description: "Very good basque food, creative dishes with terrific flavors! Donostia is a high end tapas restaurant with a friendly relaxed ambiance. Come over to enjoy awesome tapas!\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "donostia", isVisited: false), | |
Restaurant(name: "Royal Oak", type: "British", location: "2 Regency Street London SW1P 4BZ United Kingdom", phone: "343-988834", description: "Specialise in great pub food. Established in 1872, we have local and world lagers, craft beer and a selection of wine and spirits for people to enjoy. Don't forget to try our range of Young's Ales and Fish and Chips.\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "royaloak", isVisited: false), | |
Restaurant(name: "CASK Pub and Kitchen", type: "Thai", location: "22 Charlwood Street London SW1V 2DY Pimlico", phone: "432-344050", description: "With kitchen serving gourmet burgers. We offer food every day of the week, Monday through to Sunday. Join us every Sunday from 4:30 – 7:30pm for live acoustic music!\n\nSearching for great breakfast eateries and coffee? This place is for you. We open at 6:30 every morning, and close at 9 PM. We offer espresso and espresso based drink, such as capuccino, cafe latte, piccolo and many more. Come over and enjoy a great meal.", image: "caskpubkitchen", isVisited: false) | |
] | |
#endif | |
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
import SwiftUI | |
struct RestaurantDetailView1: View { | |
@State var offset = CGSize.zero | |
let restaurant: Restaurant | |
var body: some View { | |
BottomSheet(offset: self.$offset) { | |
TitleBar() | |
.background(Color.gray) | |
HeaderView(restaurant: self.restaurant) | |
DetailInfoView(icon: "map", info: self.restaurant.location) | |
.padding(.top) | |
.background(Color.white) | |
DetailInfoView(icon: "phone", info: self.restaurant.phone) | |
.background(Color.white) | |
DetailInfoView(icon: nil, info: self.restaurant.description) | |
.padding(.top) | |
.background(Color.white) | |
DetailInfoView(icon: nil, info: self.restaurant.description) | |
.padding(.top) | |
.background(Color.white) | |
} // sheet | |
} // body | |
} | |
struct RestaurantDetailView1_Previews: PreviewProvider { | |
static var previews: some View { | |
RestaurantDetailView1(offset: CGSize(width: 0, height: 300), restaurant: restaurants[0]) | |
.background(Color.black.opacity(0.3)) | |
} | |
} | |
struct HandleBar: View { | |
var body: some View { | |
Rectangle() | |
.frame(width: 50, height: 5) | |
.foregroundColor(Color(.systemGray5)) | |
.cornerRadius(10) | |
} | |
} | |
struct TitleBar: View { | |
var body: some View { | |
HStack { | |
Text("Restaurant Details") | |
.font(.headline) | |
.foregroundColor(.primary) | |
Spacer() | |
} | |
.padding() | |
} | |
} | |
struct HeaderView: View { | |
let restaurant: Restaurant | |
var body: some View { | |
Image(restaurant.image) | |
.resizable() | |
.scaledToFill() | |
.frame(height: 300) | |
.clipped() | |
.overlay( | |
HStack { | |
VStack(alignment: .leading) { | |
Spacer() | |
Text(restaurant.name) | |
.foregroundColor(.white) | |
.font(.system(.largeTitle, design: .rounded)) | |
.bold() | |
Text(restaurant.type) | |
.font(.system(.headline, design: .rounded)) | |
.padding(5) | |
.foregroundColor(.white) | |
.background(Color.red) | |
.cornerRadius(5) | |
} | |
Spacer() | |
} | |
.padding() | |
) | |
} | |
} | |
struct DetailInfoView: View { | |
let icon: String? | |
let info: String | |
var body: some View { | |
HStack { | |
if icon != nil { | |
Image(systemName: icon!) | |
.padding(.trailing, 10) | |
} | |
Text(info) | |
.font(.system(.body, design: .rounded)) | |
Spacer() | |
}.padding(.horizontal) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example from book Mastering SwiftUI