Detect when UIPickerView starts changing / moving - swift

I'm trying to react to the event that a UIPickerView started moving (not when the row was already selected).
I have searched throughout the delegate methods, and none helped. I also tried to register a notification, but couldn't figure out any that would notify as the user puts his finger on the component and starts scrolling.
Any ideas of what alternatives are there?

You can create a custom class of UIPickerView and override hitTest(point:with:). Creating a protocol, you can send the current picker through a delegate method to your controller and draw whatever you like:
protocol CustomPickerViewDelegate: class {
func didTapped(_ picker: CustomPickerView)
}
class CustomPickerView: UIPickerView {
weak var myDelegate: CustomPickerViewDelegate?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Only test for points in your needed view
if !self.point(inside: point, with: event) {
return nil
}
// Return using CustomPickerViewDelegate the current picker
// that can be used to determine which one was selected
myDelegate?.didTapped(self)
// Call super.hitTest(_: with:)
return super.hitTest(point, with: event)
}
}
Do NOT forget (in your controller: eg. YourViewController):
self.pickerView.myDelegate = self.
Create an extension of your controller the subscribes to CustomPickerViewDelegate protocol:
extension YourViewController: CustomPickerViewDelegate {
func didTapped(_ picker: CustomPickerView) {
// do what you want here
self.addBorderTo(picker: picker)
}
}
If you like you can extend the UIPickerViewDelegate (see below how you can extend base class delegate)
Extending a delegate from a base class
Good luck :]

Related

Prevent NSToolbarItem from being removed

I want to prevent certain toolbar items from being removed by the user. They should still be movable, just not removable.
I tried creating a custom subclass of NSToolbar with a custom removeItem(at:) implementation, but it seems this method is not even called if the user drags an item out of the toolbar in the customization palette.
The delegate also doesn't seem to expose functionality for this.
How can I disable removal of certain NSToolbarItems?
I am not sure if you can prevent it from being removed but you can implement the optional toolbarDidRemoveItem method and insert the item that you don't want it to be removed back:
import Cocoa
class WindowController: NSWindowController, NSToolbarDelegate {
#IBOutlet weak var toolbar: Toolbar!
override func windowDidLoad() {
super.windowDidLoad()
toolbar.delegate = self
}
func toolbarDidRemoveItem(_ notification: Notification) {
if let itemIdentifier = (notification.userInfo?["item"] as? NSToolbarItem)?.itemIdentifier,
itemIdentifier.rawValue == "NSToolbarShowColorsItem" {
toolbar.insertItem(withItemIdentifier: itemIdentifier, at: 0)
}
}
}
Since it is not super critical if they are removed in case a private API call would stop working, I opted for the private API solution.
extension NSToolbarItem {
func setIsUserRemovable(_ flag: Bool) {
let selector = Selector(("_setIsUserRemovable:"))
if responds(to: selector) {
perform(selector, with: flag)
}
}
}
This works exactly as advertised.

listening for multitouch events in OS X using the touchesBegan and touchesMoved event handlers

I am trying to record touches and finger movements that users perform on a 2018 MacBook Trackpad.
When I try to test the API and print to console when the user touches the Trackpad, I see no output.
How do I listen for touch events and get data from them?
I referred to the handling Trackpad events documentation. The handling multitouch events section of the page says:
A view by default does not accept touch events. To handle touch events, your custom view must first call the NSView method setAcceptsTouchEvents: with an argument of YES.
However, the setAcceptsTouchEvents documentation says the method is deprecated.
When I try just printing a log on detecting a touch to test if the API is working, I do not see any console output. here is my code:
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func touchesBegan(with event: NSEvent) {
print("touched!");
}
}
how do I get the console statement to print something?
I was playing around with this and here is what I figured out.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the //view.
//view.acceptsTouchEvents = true; //deprecated but works.deprecated
view.allowedTouchTypes = [NSTouch.TouchTypeMask.direct, NSTouch.TouchTypeMask.indirect];
}
and then, you can override the functions when touches are in different phases as explained in the question.
Note that to do the "allowedTouchTypes" massage in a subclass it is like this:
import Cocoa
class UnView: NSView{
override var allowedTouchTypes: NSTouch.TouchTypeMask {
get { return [] } .. as you wish
set { }
}
}

Crash when tapping on a WatchKit table row

I can display several table rows but when I tap one of those rows, the app crashes at tableView.setNumberOfRows(:)
I don't have any clue of what is going on. Any advise or tips are welcome. Thanks in advance.
I don't understand why this happens because at the initial setup this code runs without errors. I get this error only when I tap one of the table rows.
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
#IBOutlet var tableView: WKInterfaceTable!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
loadTableData()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
private func loadTableData() {
tableView.setNumberOfRows(Categories.NotLocalizedCategories.count, withRowType: "RowController")
for (index, category) in Categories.LocalizedCategories.enumerated() {
if let rowController = tableView.rowController(at: index) as? RowController {
rowController.rowLabel.setText(category)
}
}
}
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
pushController(withName: "DetailInterfaceController", context: Categories.LocalizedCategories[rowIndex])
}
}
Apparently, either tableView or the context Categories.LocalizedCategories[rowIndex] is nil. Use the if let construct or the guard statement before passing the context.
This is happening because probably the DetailInterfaceController class, more precisely in the awake method, is being called super.awake, when doing this, the InterfaceController class will be instantiated again and the awake of this and everything inside is executed. Since the table reference for the storyboard is weak, its instance of the table will be null and then it will crash. One solution to this is to remove super.awake from the controllers you push.

IOS Swift Protocol is not working or hitting breakpoint

I created a question a few days ago and was provided with protocol on how to solve an issue of passing data back and forth . I have also looked at some tutorials and have created a protocol but it is not working or even hitting the breakpoint from what I can see it should be working. I have created a protocol for my AVPlayer so that on tap it could get a new video but like i said it's not even hitting the breakpoint this is my code...
protocol CustomAVPLayerProtocol: class {
func reloadTabled(at index: Int)
}
class CustomAVPLayerC: AVPlayerViewController {
var delagate: CustomAVPLayerProtocol?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.delagate?.reloadTabled(at: 1)
for touch in touches {
self.delagate?.reloadTabled(at: 1)
print("Tapped")
// When I tap the AVPlayer this print statement shows
// So I know it is coming here
}
}
}
Now This is my second class/controller
class BookViewC: UIViewController, CustomAVPLayerProtocol {
override func viewDidLoad() {
super.viewDidLoad()
PlayVideo(250, "url")
}
func reloadTabled(at index: Int) {
print("This protocol method does not execute or hit breakpoint")
self.PlayVideo(250, "url")
}
func PlayVideo(MediaHeight: Float, MediaURL: String) {
let movieURL = URL(string: MediaURL)
videoCapHeight.constant = CGFloat(MediaHeight)
streamsModel.playerView = AVPlayer(url: movieURL!)
streamsModel.MyAVPlayer.player = streamsModel.playerView
streamsModel.MyAVPlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
streamsModel.MyAVPlayer.showsPlaybackControls = false
streamsModel.MyAVPlayer.view.frame = VideoView.bounds
VideoView.addSubview(streamsModel.MyAVPlayer.view)
self.addChildViewController(streamsModel.MyAVPlayer)
streamsModel.playerView?.isMuted = false
streamsModel.MyAVPlayer.player?.play()
}
}
As I stated before it is not even hitting the breakpoint on BookViewC.reloadTabled as suggestions would be great
As per your code these are some minor mistakes which you can correct to make it work.
1. `weak var delagate: CustomAVPLayerProtocol?`
*Make a weak delegate to save it from retaining a strong reference cycle and memory leaks.*
2. Code Snippet:
func PlayVideo {
let customPlayer = CustomAVPLayerC()
customPlayer.delegate = self
}
in Your Second ViewController, You need to assign your delegate to an object / view controller to make ie respond to
NOTE: In case you require, you can make a super class that conforms the your protocol class, so that your every view controller conforms it automatically, you just need to assign an delegate to class on which you want to use it.
You have the foundation set up correctly but remember that classes are (mostly) just blueprints for instances. These classes are useless until you create instances of them because it’s the instances that will do the work.
Therefore, simply pass one instance as the delegate of the other, which you can do here because you've correctly set up the protocol.
protocol CustomAVPLayerProtocol: AnyObject {
func reloadTabled(at index: Int)
}
class CustomAVPLayerC: AVPlayerViewController {
weak var delagate: CustomAVPLayerProtocol?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.delagate?.reloadTabled(at: 1)
for touch in touches {
self.delagate?.reloadTabled(at: 1)
print("Tapped")
}
}
}
class BookViewC: UIViewController, CustomAVPLayerProtocol {
override func viewDidLoad() {
super.viewDidLoad()
PlayVideo(250, "url")
}
func reloadTabled(at index: Int) {
PlayVideo(250, "url")
}
func PlayVideo(_ MediaHeight: Float, _ MediaURL: String) {
//
}
}
let book = BookViewC()
let layer = CustomAVPLayerC()
layer.delagate = book
Where you do this instantiation/delegation is up to you. Also, I know that a lot of people here use class to define protocols that only conform to classes, but Swift's documentation instructs us to use AnyObject.
Protocols are the most common means used by unrelated objects to communicate with each other. As per the above code, the communication did not seem to happen.
Your protocol declaration part seems alright. The problem exists in the secondViewController. I can see that you have not set the delegate to the object that's been created. Ideally, it has to be something like this:
Class BookViewC: UIViewController, CustomAVPLayerProtocol {
override func viewDidLoad() {
super.viewDidLoad()
PlayVideo(250, "url")
}
You need to the set the delegate here:
func PlayVideo {
let customPlayer = CustomAVPLayerC()
customPlayer.delegate = self //This makes the selector to respond
}

Implementing a Delegate Method in a Subclass whose Superclass conforms to that protocol Swift

I have incorporated the GoogleMaps API into my app and all is well with that. However after a bit more development I realized I will need to have two different ViewControllers, both showing a GMSMapView but each having slightly different functionality. I decided to make a base class which has the common functionality and that base class conforms to GMSMapViewDelegate. In this base class, among other things I have:
class BaseMapViewController: UIViewController {
var mapView = GMSMapView()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
loadMap()
}
func loadMap() {
mapView = GMSMapView(frame: CGRectZero)
mapView.mapType = kGMSTypeHybrid
self.view = mapView
}
}
extension BaseMapViewController: GMSMapViewDelegate {}
One of the subclasses needs to implement func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) so I just implement that in the subclass (shown below), but it doesn't register any taps.
class SellerMapViewController: BaseMapViewController {
override func viewDidLoad(){
super.viewDidLoad()
}
func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
print("\(coordinate.latitude,coordinate.longitude)")
}
}
I then tried putting that delegate method in the base class and it still didn't register any taps. Any ideas as to what I am doing wrong or what I could try?
Thanks!
It sounds like there could be a couple things going on:
Make sure you are setting the superclass as the delegate, not just conforming to the protocol.
I have done a similar things in my own code, and what I do is implement a dummy function in the superclass that if called prints a message that its subclass needs to implement it, so that the superclass fully implements the delegate method.
Implement the methods that you need to in your subclass. If the relationship was properly set up in your superclass, the methods will be called in your subclass, overriding the same call in the superclass.