How to assign UIGestureRecognizerDelegate to CKComponentController - ios

I use the next code to setup UIPanGestureRecognizer in my component:
MyComponent *c =[super newWithView:{
[UIView class],
{
{
CKComponentGestureAttribute(
[UIPanGestureRecognizer class],
&setupPanRecognizer,
#selector(panGesture:context:),
{}
)
},
{
#selector(setUserInteractionEnabled:), #YES
}
}
}
component: [MyOtherComponent newOtherComponentWithMode:model context:context]];
I process panGesture:context in MyComponentController object.
My problem is that UIPanGestureRecognizer blocks feed view from scrolling. In order to fix this I want to use UIGestureRecognizerDelegate protocol and allow both (scroll view and my pan) recognizers to work simultaneously.
My question is how can I assign my component controller as a delegate for UIPanGestureRecognizer? setupPanRecognizer is just a C function and it does not have reference to MyComponentController object or even the component itself.
The only way I see now is to get list of gesture recognisers somewhere in didUpdateComponent method in my controller, find the right one and assign delegate there.
Does ComponentKit provide any solution to this?

CKComponentViewAttributeValue CKComponentGestureAttribute(Class gestureRecognizerClass,
CKComponentGestureRecognizerSetupFunction setupFunction,
CKComponentAction action,
CKComponentForwardedSelectors delegateSelectors)
You can find the "delegateSelectors" argument at last, so you could try to pass in a vector of selectors which you would like to response in the controller.

Related

How RX button tap handling actually had been implemented?

Does anyone know how RX button tap handling actually had been implemented?
If we looked in depth into RxCocoa code. To be more specific into the "Reactive" struct, we could find it has an extension where it's base is UIButton and in this extension, there is a tap variable of type ControlEvent which return a controlEvent(.touchUpInside).
So the question is how controlEvent(.touchUpInside) handles the control events?!
controlEvent(_ controlEvents: UIControlEvents) -> ControlEvent<()> .. is a function inside extenion for "Reactive" struct where it's base is UIControl.
And when our base is UIButton, which also extend UIControl, So we can call this function too when our base is UIButton and this is our case and this the function which handles UIButton taps (which only specified in RxCocoa as in extension).
How controlEvent function work and handle touchupInside?!
controllEvent just adding target selector to UIControll throw custom ControlTarget class in RxCocoa which pass escaping Callback to UIControll to emit onNext in a specific control event (touchupInside in our case).
read this classes in RxCoca if my explanation is not good enough :)
UIButton+Rx.swift
ControlEvent.swift
UIControl+Rx.swift
ControlTarget.swift
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}

Mechanic of how observing function works in SWIFT

I believe everyone is familiar with
open func didAddSubview(_ subview: UIView)
This function is called whenever a new subView is added to the UIView.
I am wondering on how to achieve? Like, what is the underlying codes to achieve this?
Thanks
Edit:
what i mean in the question is the codes to achieve a non-objective-c observing function. For example, we can build a notification, so that whenever addSubView is called, the didAddSuView is called correspondently, but apparently, in this case. It is not an object-c selector which we see in normal implementation. So i am asking how do we achieve something like this, which means, how do i trigger a function when another function is called without it being an objc function.
Btw, just to clarify further. The function is implemented as function of UIView (Extension UIView), so it is not a protocol, which means it wasn't elegantly achieved as delegate.
So again, i am asking.. How can i replicate something like this?
“The underlying codes” is that you just call the “observing function” when it needs to be called. There's no magic in how UIKit calls didAddSubview. It doesn't use any features specific to Objective-C.
The iOS SDK has four methods for adding a subview to a view:
addSubview:
insertSubview:atIndex:
insertSubview:aboveSubview:
insertSubview:belowSubview:
All of these methods are wrappers for a private method, _addSubview:positioned:relativeTo:. You can check this using a disassembler, or by putting a breakpoint in didAddSubview: and looking at the stack trace to see who calls it.
The private method calls [self didAddSubview:subview] using a normal Objective-C message. It does not “build a notification”. The source code probably looks something like this:
- (void)_addSubview:(UIView *)newSubview position:(UIViewSubviewPosition)position relativeTo:(UIView *)sibling {
// Lots of bookkeeping related to first responder status,
// gesture recognizers, auto layout, visual effects,
// and private implementation details…
[newSubview removeFromSuperview];
switch (position) {
case UIViewSubviewPositionAtEnd:
[self.layer addSublayer:newSubview.layer];
break
case UIViewSubviewPositionBelowSibling:
[self.layer insertSublayer:newSubview.layer below:sibling.layer];
break;
case UIViewSubviewPositionAboveSibling:
[self.layer insertSublayer:newSubview.layer above:sibling.layer];
break;
default:
[self.layer insertSublayer:newSubview.layer atIndex:(unsigned int)position];
break;
}
[newSubview didMoveToSuperview];
[newSubview didMoveToWindow];
[self didAddSubview:newSubview];
// Lots more bookkeeping related to first responder status,
// gesture recognizers, auto layout, visual effects,
// and private implementation details…
}
In Swift, it could look like this:
enum SubviewPosition {
case atEnd
case below(UIView)
case above(UIView)
case atIndex(UInt32)
}
func _addSubview(_ newSubview: UIView, position: SubviewPosition) {
// Lots of bookkeeping related to first responder status,
// gesture recognizers, auto layout, visual effects,
// and private implementation details…
newSubview.removeFromSuperview()
switch position {
case .atEnd: layer.addSublayer(newSubview.layer)
case .below(let sibling):
layer.insertSublayer(newSubview.layer, below:sibling.layer)
case .above(let sibling)):
layer.insertSublayer(newSubview.layer, above:sibling.layer)
case .atIndex(let index):
layer.insertSublayer(newSubview.layer at:index)
}
newSubview.didMoveToSuperview()
newSubview.didMoveToWindow()
didAddSubview(newSubview)
// Lots more bookkeeping related to first responder status,
// gesture recognizers, auto layout, visual effects,
// and private implementation details…
}

How to disable a button when the 1st row in a custom picker view is selected?

I have a custom UIPickerView class (also conforms to UIPickerViewDelegate, UIPickerViewDataSource) that is used in 2 view controllers.
So in both of them I have this:
myPickerView.delegate = myPickerView and same with dataSource.
All the rows and components are configured in that custom picker view class.
I want to disable a button in my VCs when the 1st row is selected.
Usually I would do this by using one of the picker view's delegate functions (pickerView(_:,didSelectRow:, inComponent:), but since I use a custom one that already conforms to UIPickerViewDelegate, I can't do that.
Hmm, tricky. The issue is that the picker is its own delegate, which makes the solution a bit complicated, but here's what I might do: give the picker a closure that gets called when the selected row changes. Then call that closure from the delegate method.
class MyPickerView {
typealias SelectedIndexClosure = (Int) -> Void
var selectedIndexClosure: SelectedIndexClosure = { _ in }
...
func picker(_ picker: UIPickerView, didSelectRow row: Int ...) {
selectedIndexClosure(row)
}
}
Then you'd set the selectedIndexClosure to whatever code you want to be run in the view controller.
myPickerView.selectedIndexClosure = { [weak self] index in
self?.button.enabled = (index == 1)
}
And that should do. An more idiomatic solution might be to refactor the picker's datasource and delegate methods into a new, separate object owned by the view controller, but this should work fine.
You can say self.button.isEnabled = self.picker.selectedRow(inComponent: 0) == 0 if you just need to set it once. If you need it to be reenabled after moving the picker, or disabled if you move back to the first row, you will need to use that same delegate method.

UIView subclass access ViewController methods swift

In a couple of my projects I think I'm not created a great structure in many cases.
It could be a game where I've created a game board (think about chess) with a grid of 8 * 8 cells. Each cell has a gesture recognizer that relies on a subclass (cell.swift), with the game logic in a parent ViewController.
For arguments sake, let us say we want to display to the user which square they have touched.
I've found out how to do this from the subclassed UIView (obvs. create the alert in the subclassed UIView / cell.swift in this example)
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
but it seems to break the structure of the app - but wouldn't it be the same accessing an action in the parent ViewController? What is the best way of approaching this>
Your rootViewController is the VC on the bottom of your stack. It's not a safe way to access the visible VC, and is rarely useful, in general (there are cases, but I doubt your app would find them useful).
What you likely want to use is a delegate pattern. Let's say the parent VC that displays your chess board (let's call this MyBoardViewController), conforms to a protocol like the following. MyView is whatever custom UIView class you're using for the chess squares:
protocol SquareAlertHandler {
func handleSquarePressed(sender : myView)
}
And add the following property to your MyView class:
weak var delegate : SquareAlertHandler?
And replace whatever event handler you're currently using, with the following (I'm assuming you're using a UIButton in IB to handle the press, and have arbitrarily named the outlet 'didPress:'):
#IBAction didPress(sender : UIButton) {
delegate?.handleSquarePressed(self)
}
Now, add the protocol to your MyBoardViewController, and define the method:
class MyBoardViewController : UIViewController, SquareAlertHandler {
... ... ...
func handleSquarePressed(sender : myView) {
// Do something to handle the press, here, like alert the user
}
... ... ...
}
And finally, wherever you create the MyView instances, assign the MyBoardViewController instance as the delegate, and you're good to go.
Depending on your Swift literacy, this may be confusing. Adding code, so that I can at least match up the class names, would help to clarify things.

Swift: Best way to get value from view

I have a custom UIView (called GridView) that I initialize and then add to a ViewController (DetailViewController). GridView contains several UIButtons and I would like to know in DetailViewController when those buttons are touched. I'm new to Swift and am wondering what is the best pattern to use to get those events?
If you want to do this with notifications, use 1:
func postNotificationName(_ notificationName: String,
object notificationSender: AnyObject?)
in the method that is triggered by your button. Then, in your DetailViewController, add a listener when it is initialized with 2:
func addObserver(_ notificationObserver: AnyObject,
selector notificationSelector: Selector,
name notificationName: String?,
object notificationSender: AnyObject?)
Both functions can be called from NSNotificationCenter.defaultCenter().
Another method would be to add callbacks which you connect once you initialize the GridView in your DetailViewController. A callback is essentially a closure:
var callback : (() -> Void)?
which you can instantiate when needed, e.g.
// In DetailViewController initialization
gridView = GridView()
gridView.callback = { self.doSomething() }
In GridView you can trigger the callback like this:
func onButton()
{
callback?()
}
The callback will only execute, if unwrapping succeeds. Please ensure, that you have read Automatic Reference Counting, because these constructs may lead to strong reference cycles.
What's the difference? You can connect the callback only once (at least with the method I've showed here), but when it triggers, the receiver immediately executes its code. For notifications, you can have multiple receivers but there is some delay in event delivery.
Lets assume your GridView implementation is like as follows:
class GridView : UIView {
// Initializing buttons
let button1:UIButton = UIButton(...)
let button2:UIButton = UIButton(...)
// ...
// Adding buttons to view
self.addSubview(button1)
self.addSubview(button2)
// ...
}
Now, we will add selector methods which will be called when a button is touched. Lets assume implementation of your view controller is like as follows:
class DetailViewController : UIViewController {
let myView:GridView = GridView(...)
myView.button1.addTarget(self, action: "actionForButton1:", forControlEvents: UIControlEvents.TouchUpInside)
myView.button2.addTarget(self, action: "actionForButton2:", forControlEvents: UIControlEvents.TouchUpInside)
// ...
func actionForButton1(sender: UIButton!) {
// Your actions when button 1 is pressed
}
// ... Selectors for other buttons
}
I have to say that my example approach is not a good example for encapsulation principles of Object-Oriented Programming, but I have written like this because you are new to Swift and this code is easy to understand. If you want to prevent duplicate codes such as writing different selectors for each button and if you want to set properties of your view as private to prevent access from "outside" like I just did in DetailViewController, there are much much better solutions. I hope it just helps you!
I think you better create a class called GridView that is inherited from the UIView. Then, you can connect all you UI element with you class as IBOutlet or whatever using tag something like that. Later on, you can ask the instance of GridView in DetailViewController so that you can connect as IBAction.
Encapsulation is one of the principles of OOP.

Resources