A Shape that returns multiple options - swiftui

So what I'm thinking is if there is a way to create a Shape that when initialized can return one of any option that the caller would like. For example:
struct LetterTemplates: Shape {
init(letterTemplate: LetterTemplate) {
// This is where I'm confused. Can I somehow select which path to return based on some input by the caller??
}
func path(in rect: CGRect) -> Path { ... } // Short Letter Template
func path(in rect: CGRect) -> Path { ... } // Long Letter Template
}

What you're directly asking about is the strategy pattern. To accomplish it in a language with closures, when they need to be forwarded to methods, you do this (with real implementations instead of _ in fatalError()):
struct LetterTemplates: Shape {
init(letterTemplate: LetterTemplate) {
switch letterTemplate {
case .💌:
path = { _ in fatalError() }
case .🔠:
path = { _ in fatalError() }
}
}
private let path: (CGRect) -> Path
func path(in rect: CGRect) -> Path { path(rect) }
}
However, this is not the SwiftUI way. The SwiftUI way is to have a collection of types that all implement a particular protocol, and then, to use autocomplete with that protocol's name to pick out particular implementations.
People are trying to improve the usability of that.

You could try approaching the problem like this: define an enum with possible templates:
enum LetterTemplate {
case short
case long
}
Than use that enum to initialise an instance of LetterTemplateShape and use it to choose the right path to draw in path(in:):
struct LetterTemplateShape: Shape {
let letterTemplate: LetterTemplate
init(letterTemplate: LetterTemplate) {
self.letterTemplate = letterTemplate
}
func path(in rect: CGRect) -> Path {
switch letterTemplate {
case .short:
return ShortLetterTemplateShape().path(in: rect)
case .long:
return LongLetterTemplateShape().path(in: rect)
}
}
}
And then declare the actual paths for each case of the enum as separate shapes:
struct ShortLetterTemplateShape: Shape {
func path(in rect: CGRect) -> Path {
// actual implementation
}
}
struct LongLetterTemplateShape: Shape {
func path(in rect: CGRect) -> Path {
// actual implementation
}
}

Related

UIImpactFeedbackGenerator is Not Working? UIKit - Xcode 13.4.1 - Swift 5

I want to generate vibration when I press the button but I get no results.
Helper class I created to manage vibrations:
import Foundation
import UIKit
final class HapticsManager{
static let shared = HapticsManager()
private init(){}
public func selectionVibrate(){
DispatchQueue.main.async {
let selectionImpactGenerator = UIImpactFeedbackGenerator()
selectionImpactGenerator.prepare()
selectionImpactGenerator.impactOccurred()
}
}
public func haptic(for type: UIImpactFeedbackGenerator.FeedbackStyle){
DispatchQueue.main.async {
let notificationGenerator = UIImpactFeedbackGenerator()
notificationGenerator.prepare()
notificationGenerator.impactOccurred()
}
}
}
in ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
HapticsManager.shared.selectionVibrate()
addTargets()
setStartGradientView()
showLayout()
}
Function of button with click target added:
#objc fileprivate func setButtonClicked(){
HapticsManager.shared.haptic(for: .heavy)
}
I tried many methods but no result.
Thanks...
There is no problem about your code but needs some additional information. You need to check if device hardware is support for CHHapticEngine like that;
public func haptic(for type: UIImpactFeedbackGenerator.FeedbackStyle) {
if CHHapticEngine.capabilitiesForHardware().supportsHaptics {
let notificationGenerator = UIImpactFeedbackGenerator()
notificationGenerator.prepare()
notificationGenerator.impactOccurred()
} else {
AudioServicesPlaySystemSound(1520)
}
}

Saving ViewBuilder parameters to be used outside of init

I'm creating a custom object called AsyncImageCached using the same init signature as found in AsyncImage. My question is, how do I define variables outside of init to save content and placeholder parameters to be used when my async await calls complete?
public struct AsyncImageCached<Content> : View where Content : View {
private let content: ((Image) -> I)? <--- Doesn't Work, Cannot find type 'I' in scope
private let placeholder: (() -> P)? <--- Doesn't work, Cannot find type 'P' in scope
init<I, P>(url: URL?, scale: CGFloat = 1,
#ViewBuilder content: #escaping (Image) -> I,
#ViewBuilder placeholder: #escaping () -> P)
where Content == _ConditionalContent<I, P>, I : View, P : View {
let content: (Image) -> I = content <--- Works, but can't access outside of init
let placeholder: () -> P = placeholder <--- Works, but can't access outside of init
...
}
}
Moving I, P to the structure level will break the other inits and will not match Apples AsyncImage signatures.
There must be a way to make it work because the same signature is in AsyncImage. I don't want to change the init signature function because I already have the other inits working:
public init(url: URL?, scale: CGFloat = 1) where Content == Image
public init(url: URL?, scale: CGFloat = 1, #ViewBuilder content: #escaping (AsyncImagePhase) -> Content)
Any help would be greatly appreciated, I have spent two days on this and I can't find anything online that teaches how to use the ViewBuilder outside of simple examples, non that have a custom init like this.
Well, inspecting Swift interface file, we can see the following:
public struct AsyncImage<Content> : SwiftUI.View where Content : SwiftUI.View {
/* ... */
#_alwaysEmitIntoClient public init<I, P>(url: Foundation.URL?, scale: CoreGraphics.CGFloat = 1, #SwiftUI.ViewBuilder content: #escaping (SwiftUI.Image) -> I, #SwiftUI.ViewBuilder placeholder: #escaping () -> P) where Content == SwiftUI._ConditionalContent<I, P>, I : SwiftUI.View, P : SwiftUI.View {
self.init(url: url, scale: scale) { phase in
if let i = phase.image {
content(i)
} else {
placeholder()
}
}
}
/* ... */
}
So, it turns out it just calls another init itself, which doesn't require these generics!
You can see the interface file at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface, replacing Xcode.app with whatever the name is (as betas have different names)

Swift 3: Convert PromiseKit deferred to RxSwift

I'm currently replacing PromiseKit with RxSwift, and need to convert my deferred promise to RxSwift.
Current implementation example in PromiseKit:
private var deferredDidLayout = Promise<()>.pending()
override func layoutSubviews() {
super.layoutSubviews()
self.deferredDidLayout.fulfill()
}
func setup() {
_ = self.didLayout().then {_ -> Void in
// Do my stuff only one time!
}
}
private func didLayout() -> Promise<()> {
return self.deferredDidLayout.promise
}
Current hack-implementation in RxSwift:
private let observableDidLayout = PublishSubject<Void>()
override func layoutSubviews() {
super.layoutSubviews()
self.observableDidLayout.onCompleted()
}
func setup() {
_ = self.observableDidLayout
.subscribe(onCompleted: { _ in
// Do my stuff only one time!
// Issue: Will be executed on every onCompleted() call
})
}
Thank you in regard!
PromiseKit: https://github.com/mxcl/PromiseKit
RxSwift: https://github.com/ReactiveX/RxSwift
I believe that 'Completable' is what you are looking for - https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Traits.md#creating-a-completable

RxSwift Observable filter with casting

Disclaimer: i'm a half Rx newbie, so it is very possible that the idea is completely bonkers :)
I'm trying to write ObservableType filter, which would pass only certain type, but will pass that type, not the original sequence type. This is what i came up with so far:
extension ObservableType where Self.E: RxFilterableType {
func filterByCast<T: RxFilterableType>(class: T.Type) -> Observable<T> {
let retval = PublishSubject<T>()
self.subscribe { event in
switch event {
case .next(let element):
if let passed = element as? T {
retval.onNext(passed)
}
case .error(let error):
retval.onError(error)
case .completed:
retval.onCompleted()
}
}
return retval
}
}
func test() {
class A: RxFilterableType {}
class B: RxFilterableType {}
let array: [RxFilterableType] = [A(), B()]
let observable: Observable<RxFilterableType> = Observable.from(array)
let observableCasted: Observable<A> = observable.filterByCast(class: A.self)
}
This has two problems: the lesser problem is that the inner subscribe disposable is not taken care of. Ideally i'd like to pass the disposal responsibility onto the return value, but i can take the disposer as parameter. I don't care.
The bigger problem is the compiler objection on the last test line:
Using 'RxFilterableType' as a concrete type conforming to protocol 'RxFilterableType' is not supported
Which means, i'm afraid, that the compiler has not enough informations to infer what i'm trying to do, despite more-than-necessary hints i've added in desperate attempts to help the poor guy.
If you put this in a playground configured to use RxSwift, it will work:
import RxSwift
extension ObservableType {
func filterByCast<T>() -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted: Observable<A> = observable.filterByCast()
_ = casted.subscribe(onNext: { print($0) })
Or if you don't like specifying the type of casted:
extension ObservableType {
func filterByCast<T>(_ class: T.Type) -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted = observable.filterByCast(A.self)
_ = casted.subscribe(onNext: { print($0) })
Requiring the class type as a parameter is a nice touch of yours. I hadn't thought of doing that.

transitioningDelegate never called after Segue transition

So I'm trying to implement a custom animation as my app transitions from one View Controller to another, but for some reason the animateTransition function in my custom animation class is never called.
For the record, I'm using Xcode 8 and writing in Swift 3. The problem I'm trying to over come, is that the function is never called - I'll sort out the actual animation in the future, for now its
Here is the code in my CustomPresentAnimationController class, which should handle the transition animation...
import UIKit
class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let duration = 0.5
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
print("Checking duration")
return duration
}
func animationController(forPresented presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 1")
return self
}
func presentationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 2")
return self
}
func animationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 3")
return self
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
print("It's working!")
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {
return
}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {
return
}
let container = transitionContext.containerView
let screenOffDown = CGAffineTransform(translationX: 0, y: -container.frame.height)
let screenOffUp = CGAffineTransform(translationX: 0, y: container.frame.height)
container.addSubview(fromView)
container.addSubview(toView)
toView.transform = screenOffUp
UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
fromView.transform = screenOffDown
fromView.alpha = 0.5
toView.transform = CGAffineTransform.identity
toView.alpha = 1
}) { (success) in
transitionContext.completeTransition(success)
}
}
}
Here is the code for my ViewController (which both of my View Controllers reference)...
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {
override func viewDidLoad() {
if transitioningDelegate != nil {
print("Should do something...")
print(transitioningDelegate)
} else {
print("Transitioing Delegate set to nil")
}
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let customPresentAnimationController = CustomPresentAnimationController()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("doing our custom transition")
print(segue.destination)
let destination = segue.destination
destination.transitioningDelegate = customPresentAnimationController
}
}
When I run the code, and click on the button I provided, which links to my seance View Controller, and is set to 'Present Modally', the view changes with the standard transition (slides up from the bottom) - and the following is printed out to Xcode:
Transitioing Delegate set to nil
doing our custom transition
<moduleView.ViewController: 0x7fe427f09a40>
Should do something...
Optional(<moduleView.CustomPresentAnimationController: 0x60800002e980>)
Obviously the first line is just as the first view loads, all the rest shows that my transitionDelegate is set on the Segue destination, and is indeed loaded in as the second view loads, and that the transitionDelegate is set to CustomPresentAnimationController... yet none of the functions in that class are ever called as it never prints anything out from those functions.
Any help appreciated!
Ensure the method signature for implementing the delegate matches the updated Swift 3 syntax.