How to scroll collectionViewCell smoothly like news ticker at bottom in swift 4 ios - uicollectionview

I want to scroll collectionView cells slowly ,Smoothly and automatically like marquee in swift 4.
try to animate with this and works perfectly
func startScrolling(){
let co = collectionView.contentOffset.x
let no = co + 1
UIView.animate(withDuration: 0.001, delay: 0, options: .curveEaseInOut, animations: { [weak self]() -> Void in
self?.collectionView.contentOffset = CGPoint(x: no, y: 0)
}) { [weak self](finished) -> Void in
self?.startScrolling()
}
}
But from this not get Index and not clickable also , Need help

Related

SwiftUI: Is it possible to create macOS HUDs like the ones that comes up when changing brightness/volume or building xode projects?

I started playing with SwiftUI recently and i can't figure out how to create the kind of HUDs that come up when you build an Xcode project or when you change the screen brightness. Does anybody know how to recreate these? Here is an image for reference:
I was trying to implement this yesterday and finally kind of found a solution, maybe not perfect, but it works.
The basic idea is that you need to create a new borderless window and then close it after it is been displayed. This is the code I use, hope it helps you.
Button("Show HUD") {
// create a borderless panel
let window = NSPanel(contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), styleMask: [.fullSizeContentView, .borderless, .hudWindow], backing: .buffered, defer: false)
window.center()
// make transparent background
window.backgroundColor = .clear
let rect = NSScreen.main!.frame
// move the window to bottom center
let frame = NSRect(origin: window.frame.offsetBy(dx: 125, dy: -rect.height / 4).origin, size: window.frame.size)
window.setFrame(frame, display: true)
// create hosting view
window.contentView = NSHostingView(rootView: HUDView())
// show the window
window.makeKeyAndOrderFront(nil)
// Auto close the window after 1.8s
DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) {
window.close()
}
}
And then define your HUDView
struct HUDView: View {
#State var showHUD: Bool = false
var body: some View {
ZStack {
VStack {}
.background(.clear)
.frame(width: 200, height: 200)
.onAppear {
withAnimation(.easeInOut(duration: 0.3)) {
showHUD = true
}
}
if showHUD {
VStack(spacing: 20) {
Image(systemName: "doc.on.clipboard.fill").font(.system(size: 72))
Text("Code Copied").font(.system(size: 20))
}
.frame(width: 200, height: 200)
// the background could be .regularMaterial, but for some reason it can not works, so instead I created my own BlurView...
.background(BlurView())
.cornerRadius(20)
.foregroundColor(.white.opacity(0.8))
.overlay(RoundedRectangle(cornerRadius: 20).strokeBorder(.white.opacity(0.15), lineWidth: 1))
}
}
}
}
BlurView defines
struct BlurView: NSViewRepresentable {
func makeNSView(context: Context) -> NSVisualEffectView {
let blurView = NSVisualEffectView()
blurView.blendingMode = .behindWindow
blurView.isEmphasized = true
blurView.material = .hudWindow
blurView.autoresizingMask = [.width, .height]
blurView.state = NSVisualEffectView.State.active
return blurView
}
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
}
}
The preview should show something like below, pretty cool 😎 , isn't it?

How to use other objc function's variable? (Long Press & Add a Button)

Is there any way to use other objc function's variable? Here is the scenario: When I long-press on some point of the view, then menu items come up, and when I press Add button, then I want to create a circle button on that position
//Menu Items
#objc func longPressForView(sender: UILongPressGestureRecognizer) -> CGPoint {
if sender.state == .began {
let menu = UIMenuController.shared
becomeFirstResponder()
let menuItemAdd = UIMenuItem(title: "Add", action: #selector(addCircleMenuItemAction))
let menuItemDelete = UIMenuItem(title: "Delete", action: #selector(handleMenuItemAction))
menu.menuItems = [menuItemAdd, menuItemDelete]
let location = sender.location(in: sender.view)
let menuLocation = CGRect(x: location.x, y: location.y, width: 0, height: 0)
menu.showMenu(from: sender.view!, rect: menuLocation)
//I tried to return this location but I don't know how to use it
return location
}
}
//Click Menu Items' Add Button to add a circle on that position.
#objc func addCircleMenuItemAction() {
let longPressedlocation = longPressForView(sender: )
print("Add item tapped")
view.addSubview(addCircle)
addCircle.layer.cornerRadius = CGFloat(circleAddDefaultSize/2)
addCircle.translatesAutoresizingMaskIntoConstraints = false
addCircle.backgroundColor = .black
//Here is where I want to use the location to add a circle
addCircle.frame = CGRect(origin: longPressedlocation?????, size: CGSize(width: circleAddDefaultSize,height: circleAddDefaultSize))
You can create a global variable and use that inside both functions, something like this
//Menu Items
var location: CGPoint
#objc func longPressForView(sender: UILongPressGestureRecognizer) -> CGPoint {
if sender.state == .began {
let menu = UIMenuController.shared
becomeFirstResponder()
let menuItemAdd = UIMenuItem(title: "Add", action: #selector(addCircleMenuItemAction))
let menuItemDelete = UIMenuItem(title: "Delete", action: #selector(handleMenuItemAction))
menu.menuItems = [menuItemAdd, menuItemDelete]
let location = sender.location(in: sender.view)
let menuLocation = CGRect(x: location.x, y: location.y, width: 0, height: 0)
menu.showMenu(from: sender.view!, rect: menuLocation)
//I tried to return this location but I don't know how to use it
self.location = location
return location
}
}
#objc func addCircleMenuItemAction() {
print("Add item tapped")
view.addSubview(addCircle)
addCircle.layer.cornerRadius = CGFloat(circleAddDefaultSize/2)
addCircle.translatesAutoresizingMaskIntoConstraints = false
addCircle.backgroundColor = .black
//Here is where I want to use the location to add a circle
addCircle.frame = CGRect(origin: self.location, size: CGSize(width: circleAddDefaultSize,height: circleAddDefaultSize))
}
the problem with your code is, when you long-press the view, the longPressForView gets executed and when you hit the button, it gets executed again inside the function: let longPressedlocation = longPressForView(sender: ), in this line. so the output won't be the same.

How do I restrict a dragging gesture to one direction only in SwiftUI?

I've embarrassingly spent the past 2 weeks trying to solve this.
What I'm trying to do is:
Snap my Slide Over View to the bottom of the screen
Disable dragging up and only allow the card to be dragged down to close
What I've tried:
I've tried messing with the size of the card by setting its height to the height of the screen. You can see this line commented out. After doing this, I've messed around with the offset of the card and set it so that it looks as if the card is actually less than half its size, around 300 in height. The problem with this is when I slide up slowly, I can see the empty space that is hidden out of the screen. This isn't the effect I want.
The next thing I have tried to do is change the height of the card to a desired height. Then adjust the offset so the card is where I want it to be. However, I feel manually adjusting it won't be reliable on different screens. So I'm trying to work out the right math needed to always have it be placed at the very bottom of the screen when it pops up.
Finally, I want to just make it so users can only drag down and not up.
I would really appreciate some help here. I've spent a lot of time message around and reading, learning new things, but I can't solve my specific problem.
Here is my Slide Over Card
import SwiftUI
struct SigninView<Content: View> : View {
#GestureState private var dragState = DragState.inactive
#State var position = CardPosition.top
var content: () -> Content
var body: some View {
let drag = DragGesture()
.updating($dragState) { drag, state, transaction in
state = .dragging(translation: drag.translation)
}
.onEnded(onDragEnded)
return Group {
// Handle()
self.content()
}
.frame(height: 333) //UIScreen.main.bounds.height)
.background(Color.purple)
.cornerRadius(10.0)
.shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
.offset(y: self.position.rawValue + self.dragState.translation.height)
.animation(self.dragState.isDragging ? nil : .interpolatingSpring(stiffness: 300.0, damping: 30.0, initialVelocity: 10.0))
.gesture(drag)
}
private func onDragEnded(drag: DragGesture.Value) {
let verticalDirection = drag.predictedEndLocation.y - drag.location.y
let cardTopEdgeLocation = self.position.rawValue + drag.translation.height
let positionAbove: CardPosition
let positionBelow: CardPosition
let closestPosition: CardPosition
if cardTopEdgeLocation <= CardPosition.middle.rawValue {
positionAbove = .top
positionBelow = .middle
} else {
positionAbove = .middle
positionBelow = .bottom
}
if (cardTopEdgeLocation - positionAbove.rawValue) < (positionBelow.rawValue - cardTopEdgeLocation) {
closestPosition = positionAbove
} else {
closestPosition = positionBelow
}
if verticalDirection > 0 {
self.position = positionBelow
} else if verticalDirection < 0 {
self.position = positionAbove
} else {
self.position = closestPosition
}
}
}
enum CardPosition: CGFloat {
case top = 100
case middle = 790
case bottom = 850
}
enum DragState {
case inactive
case dragging(translation: CGSize)
var translation: CGSize {
switch self {
case .inactive:
return .zero
case .dragging(let translation):
return translation
}
}
var isDragging: Bool {
switch self {
case .inactive:
return false
case .dragging:
return true
}
}
}
Here is my ContentView page, where I test it:
import SwiftUI
struct ContentView: View {
#State var show:Bool = false
var body: some View {
SigninView {
VStack {
Text("TESTING")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
First, you should probably not have your SigninView be your content view. Instead, consider presenting your sign in view as an overlay instead.
var body: some View {
ZStack {
Text("Content here!")
}
.overlay(
SigninView()
.offset(...),
alignment: .bottom
)
}
This will automatically place your view at the bottom of the screen at the height of your SigninView, there should be little to no math involved here. The offset, you will define with your gesture and any space you want to exist between the bottom and your overlay.
Next, to only allow down gestures, can't you just clamp your translation?
var translation: CGSize {
switch self {
case .inactive:
return .zero
case .dragging(let translation):
return max(0, translation) // clamp this to the actual translation or 0 so it can't go negative
}
}

Without Bridging to ObjectiveC, Can We Get the Coordinates of a Tap Solely in SwiftUI?

I have the following code:
struct MyLocationMap: View {
#EnvironmentObject var userData: UserData
#State var annotationArray: [MyAnnotation] = [MyAnnotation(coordinates: CLLocationCoordinate2D(latitude: CurrentLocation().coordinates?.latitude ?? CLLocationCoordinate2D().latitude, longitude: CurrentLocation().coordinates?.longitude ?? CLLocationCoordinate2D().longitude), type: .waypoint)]
var body: some View {
MakeMapView(annotationArray: annotationArray)
.gesture(
LongPressGesture(minimumDuration: 1)
.onEnded { _ in
print("MapView pressed!\n--------------------------------------\n")
//Handle press here
})
}
}
import SwiftUI
import CoreLocation
import MapKit
struct MakeMapView : UIViewRepresentable {
typealias UIViewType = MKMapView
let annotationArray: [MyAnnotation]?
func makeUIView(context: UIViewRepresentableContext<MakeMapView>) -> MKMapView{
MKMapView()
}
func updateUIView(_ mapView: MKMapView, context: Context) {
mapView.showsUserLocation = true
if let coordinates = CurrentLocation().coordinates {
// updateAnnotations(from: mapView)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
mapView.showsUserLocation = true
mapView.showsCompass = true
mapView.showsScale = true
mapView.mapType = .satellite
let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
let region = MKCoordinateRegion(center: coordinates, span: span)
mapView.setRegion(region, animated: true)
})
}
}
What I am having trouble with is implementing func convert(_ point: CGPoint, toCoordinateFrom view: UIView?) -> CLLocationCoordinate2D to obtain the lat/lon of the gesture solely using SwiftUI/Combine. Is it possible? If not, can I implement it with what I have?
I have reviewed the post at
How to handle touch gestures in SwiftUI in Swift UIKit Map component?
and
Add single pin to Mapkit with SwiftUI
Once I have the coordinates, dropping the pin is straightforward, but I can't wrap my head around getting the coordinates.
Thanks.
A DragGesture with no minimumDistance activates immediately and has location and startLocation in its onChanged listener, you can use that to grab location like so:
struct GesturesView: View {
#State private var location: CGPoint = .zero
var body: some View {
let drag = DragGesture(minimumDistance: 0).onChanged {
self.location = $0.startLocation
}
let longPress = LongPressGesture().onEnded { _ in
self.doSomething(with: self.location)
}
let gesture = longPress.simultaneously(with: drag)
return Rectangle()
.frame(width: 300, height: 300)
.padding()
.gesture(gesture)
}
func doSomething(with location: CGPoint) {
print("Pressed at", location)
}
}

Add Navigation Bar in UICollectionView in swift 4, iOS 11

After upgrading the iOS 11 and swift 4(Xcode 9), I got lots of UI problem. :(
The most unsolvable problem is Navigation Problem. I put the Navigation Bar in UICollectionView Controller. Navigation bar height does not show properly and however I write the code.
I set navigation bar height "100". I write this code in ViewDidLoad().
if #available(iOS 11.0, *) {
print("IOS 11 Screen")
UINavigationBar.appearance().largeTitleTextAttributes = [
NSForegroundColorAttributeName: UIColor.white,
]
navigationItem.largeTitleDisplayMode = .never
let height: CGFloat = 100 //whatever height you want
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
} else {
print("lower than IOS 11 Screen")
// Fallback on earlier versions
}
collectionview?.frame = CGRect(x: 0, y: 10, width: UIScreen.main.bounds.width, height: (UIScreen.main.bounds.height + 20)) <br>
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let height: CGFloat = 100 //whatever height you want
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}
override func viewWillAppear(_ animated: Bool) {
let height: CGFloat = 100 //whatever height you want
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}
However, I tried with this code. Nothing works for me.
I also tried to open the storyboard with source code view and I edit the height of Navigation bar. But, it also does not work.
Can anyone help me please? I have been trying to solve this problem since last two days.
Please check :
override func viewDidLoad() {
if #available(iOS 11.0, *) {
print("IOS 11 Screen")
UINavigationBar.appearance().largeTitleTextAttributes = [
NSForegroundColorAttributeName: UIColor.white,
]
self.navigationController?.navigationBar.prefersLargeTitles = true // added this line
navigationItem.largeTitleDisplayMode = .never
} else {
print("lower than IOS 11 Screen")
// Fallback on earlier versions
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let height: CGFloat = 100 //whatever height you want
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}