Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play keyboard click sound in a collection view controller

I created a subclass of a UICollectionViewController that is used as the custom inputAccessoryViewController in a UITextView. https://developer.apple.com/reference/uikit/uiresponder/1621124-inputaccessoryviewcontroller

I want to play the keyboard click sound when you tap a cell in the collection view using playInputClick. https://developer.apple.com/reference/uikit/uidevice/1620050-playinputclick

I cannot figure out how to get this to work in a collection view. It works for a simple view like this using the inputAccessoryView property of a UITextView but I'm not sure what view to subclass in the collection view controller hierarchy to get the keyboard click sound to play.

@interface KeyboardClickView : UIView <UIInputViewAudioFeedback>
@end

@implementation KeyboardClickView
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if(self)
    {
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
        [self addGestureRecognizer:tap];
    }
    return self;
}

- (void)tap:(id)sender
{
    [[UIDevice currentDevice] playInputClick];
}

- (BOOL)enableInputClicksWhenVisible
{
    return YES;
}
@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{   
    _inputAccessoryView = [[KeyboardClickView alloc] initWithFrame:CGRectMake(0, 0, 0, 50)];
    _inputAccessoryView.backgroundColor = [UIColor redColor];
    [[UITextView appearance] setInputAccessoryView:_inputAccessoryView];

    // ...
}
@end

I'm also aware that you can play the keyboard click sound using AudioServicesPlaySystemSound(1104) but this doesn't respect the user's settings if they have the keyboard click sounds disabled.

like image 762
Berry Blue Avatar asked Aug 29 '16 01:08

Berry Blue


1 Answers

To use the benefits of playInputClick in UIViewController:

Dummy input accessory view:

@interface Clicker : UIView <UIInputViewAudioFeedback>

@end

@implementation Clicker

- (BOOL)enableInputClicksWhenVisible
{
    return YES;
}

@end

View with input accessory view:

@interface ControllerView : UIView

@end

@implementation ControllerView

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (UIView *)inputAccessoryView
{
    return [[Clicker alloc] init];
}

@end

View Controller with custom view:

@implementation ViewController

- (void)loadView
{
    self.view = [[ControllerView alloc] init];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.view becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self.view resignFirstResponder];
}

@end

When the responder object becomes the first responder and inputView (or inputAccessoryView) is not nil, UIKit animates the input view into place below the parent view (or attaches the input accessory view to the top of the input view).

There is no visual consequences since the height of Clicker view is zero, and conforming to UIInputViewAudioFeedback protocol enables [[UIDevice currentDevice] playInputClick] functionality within ViewController.

Look here for responder chains and here for input accessory views.

like image 128
bteapot Avatar answered Oct 08 '22 18:10

bteapot