Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fatbobman committed Aug 6, 2020
0 parents commit 31583b2
Show file tree
Hide file tree
Showing 14 changed files with 1,261 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>SwipeCell.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>SwipeCell</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>SwipeCellTests</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
16 changes: 16 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "Introspect",
"repositoryURL": "https://github.com/timbersoftware/SwiftUI-Introspect.git",
"state": {
"branch": null,
"revision": "de5c32c15ae169cfcb27397ffb2734dcd0e1e6d5",
"version": "0.1.0"
}
}
]
},
"version": 1
}
30 changes: 30 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "SwipeCell",
platforms: [.iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "SwipeCell",
targets: ["SwipeCell"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(name:"Introspect",url:"https://github.com/timbersoftware/SwiftUI-Introspect.git",from:"0.1.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "SwipeCell",
dependencies: ["Introspect"]),
.testTarget(
name: "SwipeCellTests",
dependencies: ["SwipeCell"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SwipeCell

A description of this package.
127 changes: 127 additions & 0 deletions Sources/SwipeCell/ScrollNotification.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//
// File.swift
//
//
// Created by Yang Xu on 2020/8/4.
//

import Foundation
import SwiftUI
import Introspect
import Combine

//这个版本的dismiss响应及时,不过会产生和SwiftUI List的一些冲突,导致删除和选择会有问题.所以屏蔽的删除.如果你不需要选择并自己实现删除,这个版本会给你最快速的滚动后SwipeButton复位动作
extension View{
public func dismissSwipeCellFast() -> some View{
self
.modifier(ScrollNotificationInject(showSelection:false))
}
}

struct ScrollNotificationInject:ViewModifier{
var showSelection:Bool
@ObservedObject var delegate = Delegate()
func body(content: Content) -> some View {
content
.introspectTableView{ list in
list.delegate = delegate
list.allowsSelection = showSelection
}
}
}

import UIKit
class Delegate:NSObject,UITableViewDelegate, UIScrollViewDelegate,ObservableObject{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
NotificationCenter.default.post(name: .swipeCellReset, object: nil)
}

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView){
NotificationCenter.default.post(name: .swipeCellReset, object: nil)
}

func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle{
return UITableViewCell.EditingStyle.none
}
}


//这个版本对于SwiftUI的List支持更好一点(可以支持选择),.不过响应稍有延迟.另外,屏幕上的Cell必须要滚动至少一个才能开始dismiss
//如果在ForEach上使用了onDelete,系统会自动在Cell右侧添加删除按钮替代自定义的swipeButton.
struct ScrollNotificationWithoutInject:ViewModifier{
@State var timer = Timer.publish(every: 0.5, on: .main, in: .common)
@State var cancellable:Set<AnyCancellable> = []
@State var listView = UITableView()
@State var hashValue:Int? = nil

func body(content: Content) -> some View {
content
.introspectTableView{ listView in

self.listView = listView
}
.onAppear{
timer = Timer.publish(every: 1, on: .main, in: .common)
timer.connect()
.store(in: &cancellable)
}
.onDisappear{
cancellable = []
}
.onReceive(timer){ _ in
if hashValue == nil {
hashValue = listView.visibleCells.first.hashValue
}
if hashValue != listView.visibleCells.first.hashValue {
NotificationCenter.default.post(name: .swipeCellReset, object: nil)
hashValue = listView.visibleCells.first.hashValue
}
}
}
}

extension View{
public func dismissSwipeCell() -> some View{
self
.modifier(ScrollNotificationWithoutInject())
}
}


//ScrollView使用的dismiss.当前在ios13下使用没有问题,不过Introspect在iOS14的beta下无法获取数据.相信过段时间便能修复.
struct ScrollNotificationForScrollView:ViewModifier{
@State var timer = Timer.publish(every: 0.5, on: .main, in: .common)
@State var cancellable:Set<AnyCancellable> = []
@State var scrollView = UIScrollView()
@State var offset:CGPoint? = nil
func body(content: Content) -> some View {
content
.introspectScrollView{scrollView in
self.scrollView = scrollView
}
.onAppear{
timer = Timer.publish(every: 1, on: .main, in: .common)
timer.connect()
.store(in: &cancellable)
}
.onDisappear{
cancellable = []
}
.onReceive(timer){ _ in
if offset == nil {
offset = scrollView.contentOffset
}
if scrollView.contentOffset != offset {
offset = scrollView.contentOffset
NotificationCenter.default.post(name: .swipeCellReset, object: nil)
}
}
}
}

extension View{
public func dismissSwipeCellForScrollView() -> some View{
self
.modifier(ScrollNotificationForScrollView())
}
}
135 changes: 135 additions & 0 deletions Sources/SwipeCell/SwipeCellConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// File.swift
//
//
// Created by Yang Xu on 2020/8/4.
//

import Foundation
import SwiftUI

public enum SwipeCellSlotPosition:Int{
case left,right,both,none
}

public enum SwipeCellSlotStyle{
case normal,destructive
}

public enum SwipeButtonStyle{
case title,image,titleAndImage,view
}

public struct SwipeCellButton{
public let buttonStyle:SwipeButtonStyle
public let title:LocalizedStringKey?
public let systemImage:String?
public let titleColor:Color
public let imageColor:Color
public let view:(() -> AnyView)?
public let backgroundColor:Color
public let action:()->Void
public let feedback:Bool

public init(buttonStyle:SwipeButtonStyle,title:LocalizedStringKey?,systemImage:String?,titleColor:Color = .white,imageColor:Color = .white,view:(() -> AnyView)?,backgroundColor:Color,action:@escaping ()->Void,feedback:Bool = true){
self.buttonStyle = buttonStyle
self.title = title
self.systemImage = systemImage
self.titleColor = titleColor
self.imageColor = imageColor
self.view = view
self.backgroundColor = backgroundColor
self.action = action
self.feedback = feedback
}
}

public struct SwipeCellSlot{
public let buttonWidth:CGFloat //按钮宽度
public let slots:[SwipeCellButton] //按钮数据
public let slotStyle:SwipeCellSlotStyle //是否包含销毁按钮,销毁按钮只能是最后一个添加
public let appearAnimation:Animation
public let dismissAnimation:Animation

public init(slots:[SwipeCellButton],slotStyle:SwipeCellSlotStyle = .normal,buttonWidth:CGFloat = 74,sticky:Bool = true,appearAnimation:Animation = .easeOut(duration:0.5),dismissAnimation:Animation = .interactiveSpring()){
self.buttonWidth = buttonWidth
self.slots = slots
self.slotStyle = slotStyle
self.appearAnimation = appearAnimation
self.dismissAnimation = dismissAnimation
}

}

public struct SwipeCellStyle{
public let destructiveWidth:CGFloat
public let dismissWidth:CGFloat
public let appearWidth:CGFloat
public let alignment:Alignment
public let vibrationForButton:Vibration
public let vibrationForDestructive:Vibration
public let autoResetTime:TimeInterval?

public init(alignment:Alignment,dismissWidth:CGFloat,appearWidth:CGFloat,destructiveWidth:CGFloat = 180,vibrationForButton:Vibration,vibrationForDestructive:Vibration,autoResetTime:TimeInterval? = nil){
self.destructiveWidth = destructiveWidth
self.appearWidth = appearWidth
self.dismissWidth = dismissWidth
self.alignment = alignment
self.vibrationForButton = vibrationForButton
self.vibrationForDestructive = vibrationForDestructive
self.autoResetTime = autoResetTime
}

public static func defaultStyle() -> SwipeCellStyle{
SwipeCellStyle(alignment: .leading,dismissWidth: 30, appearWidth: 30, destructiveWidth: 220,vibrationForButton:.soft,vibrationForDestructive:.medium,autoResetTime: nil)
}
}

import AudioToolbox

public enum Vibration {
case error
case success
case warning
case light
case medium
case heavy
@available(iOS 13.0, *)
case soft
@available(iOS 13.0, *)
case rigid
case selection
case oldSchool
case mute

public func vibrate() {
switch self {
case .error:
UINotificationFeedbackGenerator().notificationOccurred(.error)
case .success:
UINotificationFeedbackGenerator().notificationOccurred(.success)
case .warning:
UINotificationFeedbackGenerator().notificationOccurred(.warning)
case .light:
UIImpactFeedbackGenerator(style: .light).impactOccurred()
case .medium:
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
case .heavy:
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
case .soft:
if #available(iOS 13.0, *) {
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
}
case .rigid:
if #available(iOS 13.0, *) {
UIImpactFeedbackGenerator(style: .rigid).impactOccurred()
}
case .selection:
UISelectionFeedbackGenerator().selectionChanged()
case .oldSchool:
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
case .mute:
break
}
}
}
Loading

0 comments on commit 31583b2

Please sign in to comment.