Swift: Haptic effects for grid of UIButtons - uibutton

I have a grid of UIButtons and I'd like to call a haptic effect for the following methods:
When the user moves their finger over a button for the first time
When the user lifts their finger (a typical tap, .touchUpInside works perfectly)
Any suggestions for how to call a haptic effect when the user moves over a button for the first time?
button.addTarget(self, action: #selector(tap), for: .touchUpInside)
button.addTarget(self, action: #selector(moveOver), for: .??)
func moveOver(sender:UIButton) -> Bool {
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
return true
}

The correct UIEventAction to use in this case is touchDragInside

Related

How to dismiss UIAlertController on outside touch without UIalertactionstyle cancel in ios

Hi... I am having an app in which I am using UIAlerController to show alert view.
I am sucessfully able to show it but the problem is I can not dismiss the view on touching outside. I have to create a view manually and a cancel button (shown in screenshot).
I know I can get the touch with putting Uialertactionstyle cancel but that makes my UI improper.
So Is there any other way so that I can get the touch event on outside touch?
Thanks in advance.
I try to use xcode tool to see the view hierarchy ,and it looks like as below:
I find the correct view to add tap gesture,so I modify the code and it works.
UIAlertController alert;
partial void UIButtonTouchUpInside(UIButton sender)
{
alert = UIAlertController.Create("title", "msg", UIAlertControllerStyle.ActionSheet);
this.PresentViewController(alert, true, () =>
{
UITapGestureRecognizer tap = new UITapGestureRecognizer(tapAction);
alert.View.Superview.Subviews[0].AddGestureRecognizer(tap);
});
}
void tapAction()
{
alert.DismissViewController(true, null);
}

Trigger buttons in Touch Bar when sliding

I have created a Touch Bar with many colourful buttons.
The user will often change the color using the buttons.
When a button is pressed some graphical elements of the current window change accordingly with the color of the button.
I would like that sliding the finger among the buttons the elements will change their color, without having to wait for a "touch up inside" event.
Is there a quick way to do that?
EDIT: I think that a way to do that would be use a button of type momentaryPushIn or to create a class subclassing a button and handing something like a "touch inside" event.
I would prefer the first method but I am finding a very poor documentation.
Since you approved Scrubber solution here it is:
Firstly you need to screate instance of NSScrubber and insert it into your touch bar. Make sure that selection mode will be fixed.
yourScrubber.mode = .fixed
Then for purposes of highlighting(just to see outline be eyes, without this outline showed everything will work correctly) set Selection Overlay Style to .outlineOverlay.
youtScrubber.mode = .selectionBackgroundStyle = .outlineOverlay
Next set delegates for data source and for selection events.
yourScrubber.delegate = self
yourScrubber.dataSource = self
Then return amount of items in scrubber
public func numberOfItems(for scrubber: NSScrubber) -> Int {
return 8
}
Now you need to insert items(NSScrubberItemView). Which can be also filled with NSScrubberImageItemView or NSScrubberTextItemView or even your custom NSView.
There are many ways how to fill content of scrubber, for easy case where you will provide images it will look like this:
public func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
let itemView = scrubber.makeItem(withIdentifier: "colorIdentifier", owner: nil) as! NSScrubberImageItemView
itemView.image = yourImage
return itemView
}
Now you should be able to see something like this: (I used random images for my scrubber)
Also you should be able to visible(if you enabled outline) select items:
Ok and now the most important part, how to get the effect you desire: (use of delegate protocol method)
public func scrubber(_ scrubber: NSScrubber, didHighlightItemAt highlightedIndex: Int) {
print("highlight = \(highlightedIndex)")
//your code here
}
Which results to something like this: (notice output window)
You can also add spacing between cells be defining cell width:
func scrubber(_ scrubber: NSScrubber, layout: NSScrubberFlowLayout, sizeForItemAt itemIndex: Int) -> NSSize {
return NSSize(width: yourWidth, height: 30)
}
Also maybe you will find use for:
optional public func scrubber(_ scrubber: NSScrubber, didSelectItemAt selectedIndex: Int)

Override JSQMessagesInputToolbar in JSQMessagesViewController

While button is pressed I'd like to show upon the existing JSQMessagesInputToolbar new view with timer and slider for cancellation.
One of the solution I found is overriding xib files but
all UI in the project is written in Objective C, so there is no xib files I could override.
Could you please advice alternative programmatic solution to implement it?
Thank you
//MARK:- Input Toolbar Initialization
private func initRightToolBarView() -> Void {
micButton = UIButton(frame: inputButtonFrame)
micButton.setImage(CHAT_MIC_BUTTON,for:UIControlState())
micButton.addTarget(self, action: #selector(startRecording), for: .touchDown)
micButton.addTarget(self, action: #selector(endRecording), for: .touchUpInside)
micButton.addTarget(self, action: #selector(cancelRecording), for: .touchDragExit)
inputToolbar.contentView!.rightBarButtonContainerView!.addSubview(micButton)
let timerLabelFrame = inputToolbar.contentView!.textView!.frame
timerLabel = UILabel(frame: timerLabelFrame)
timerLabel.text = "00:00"
timerLabel.textAlignment = .center
timerLabel.isHidden=true
inputToolbar.contentView!.addSubview(timerLabel)
}
Now when the user starts recording then
inputToolbar.contentView!.textView!.isHidden = true
timerLabel.isHidden=false
when recording ends
inputToolbar.contentView!.textView!.isHidden = false
timerLabel.isHidden=true

tvOS: Focus first button as preferred focus view when buttons are in horizontal order

I have been trying to focus the first button but focus engine takes the third button right below the tab bar as the first item to be focussed. I tried with preferred focus view but found that when i place the buttons in vertical order then preferred takes the preferred view to be focussed but when i placed all the buttons in horizontal plane it always takes the third button.The other approach i can think of if Focus Guide but i wonder how that will work in this scenario?
override weak var preferredFocusedView: UIView? {
get {
return thrirdButton
}
}
It happens because focus engine takes the nearest possible focus element as 1st element as you can see from the picture attached.I have attached the context screenshot for the view controller. Any help or clue to solve this will be appreciated.
Solution :
We need to add focus guide just above button 3 and redirect it to button one when the focus guide is focussed.
private var focusGuide = UIFocusGuide()
self.view.addLayoutGuide(focusGuide)
self.focusGuide.bottomAnchor.constraintEqualToAnchor(self.button3.topAnchor).active = true
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.button3.leftAnchor).active = true
// Width and height
self.focusGuide.widthAnchor.constraintEqualToAnchor(self.button3.widthAnchor).active = true
self.focusGuide.heightAnchor.constraintEqualToAnchor(self.button3.heightAnchor).active = true
focusGuide.centerXAnchor.constraintEqualToAnchor(self.button3.centerXAnchor).active = true
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
self.focusGuide.preferredFocusedView = button1
}
Try adding a UIFocusGuide just above the button rows. In that case, before reaching the button, it will hit your focus guide in which you can redirect the preferredFocusedView to that particular button. The code to redirect is to be done by overriding didUpdateFocusInContext method.
Assuming you have setup the focus guide in viewDidload, the following is a sample,
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)
self.focusGuide.preferredFocusedView = self.mypreferredbutton
}
TO initialise a focus guide ( Do the addition of guide to view in viewDidLoad)
var focusGuide = UIFocusGuide()
self.view.addLayoutGuide(self.sidefocusGuide)
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.placerLabel.leftAnchor).active = true
self.focusGuide.rightAnchor.constraintEqualToAnchor(self.placerLabel.rightAnchor).active = true
self.focusGuide.topAnchor.constraintEqualToAnchor(self.audioTable.topAnchor).active = true
I too faced a similar issue but somehow able to do it through this. The preferredFocusedView approach will also work, but you have to do lot of circus by updating some reference variable and then calling setneedsfocusupdate for that view. Try the focus guide way. Hope it helps.
EDITED:
I have added code how to setup the guide. In this case I have put the guide on the right side of my view. So, whenever your focus hits the guide, it redirects to the preferredfocusguide view that you want the focus to go to in the didUpdateFocusInContext. "There is only one view in focus at anytime.", remember thes ? So, the moment it hits the guide, the overriden method gets hit which in turn moves your focus to the guide's preferred view. For examples, you can refer to Apple's UIKitCatlog app for tvOS and here is one link explaining the same.
The best way to give the initial focus to your preferred view is by using preferredFocusEnivronments.
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [firstButton]
}
preferredFocusEnvironments will be called before didUpdateFocus, that way you can inform the system, where you want the focus to be redirected.

How to toggle visibility of NSSplitView subView + hide Pane Splitter divider?

We have a parent Split view (NSSplitView), and two subviews, Content and SideBar (the sidebar is on the right).
What would be the optimal Cocoa-friendly way to toggle the SideBar view?
I would really love it, if the suggested solution includes animation
I really don't need any suggestions related to external plugins, etc (e.g. BWToolkit)
HINT : I've been trying to do that, but still I had issues hiding the divider of the NSSplitView as well. How could I do it, while hiding it at the same time?
Here's a pretty decent tutorial that shows how to do this: Unraveling the Mysteries of NSSplitView.
Hiding the divider is done in NSSplitView's delegate method splitView:shouldHideDividerAtIndex:.
You will have to animate the frame size change yourself if you don't like the way NSSplitView does it.
Easiest way to do it is as follows - and it's animated: [SWIFT 5]
splitViewItems[1].animator().isCollapsed = true // Show side pane
splitViewItems[1].animator().isCollapsed = false // hide side pane
I wrote a Swift version of the content in the link from #Nathan's answer that works for me. In the context of my example splitView is set elsewhere, probably as an instance property on an encompassing class:
func toggleSidebar () {
if splitView.isSubviewCollapsed(splitView.subviews[1] as NSView) {
openSidebar()
} else {
closeSidebar()
}
}
func closeSidebar () {
let mainView = splitView.subviews[0] as NSView
let sidepanel = splitView.subviews[1] as NSView
sidepanel.hidden = true
let viewFrame = splitView.frame
mainView.frame.size = NSMakeSize(viewFrame.size.width, viewFrame.size.height)
splitView.display()
}
func openSidebar () {
let sidepanel = splitView.subviews[1] as NSView
sidepanel.hidden = false
let viewFrame = splitView.frame
sidepanel.frame.size = NSMakeSize(viewFrame.size.width, 200)
splitView.display()
}
These functions will probably methods in a class, they are for me. If your splitView can be nil you obviously have to check for that. This also assumes you have two subviews and the one at index 1, here as sidePanel is the one you want to collapse.
In Xcode 9.0 with Storyboards open Application Scene select View->Menu->Show sidebar. CTRL-click Show Sidebar, in sent actions delete the provided one, click on x. From the circle CTRL drag to First Responder in application scene and select toggleSideBar to connect to. Open storyboard and select the first split view item and in attributes inspector change behaviour from default to sidebar. Run and try with view menu item show/hide. All done in interface builder no code. toggleSideBar handles the first split view item. https://github.com/Dis3buted/SplitViewController
I got some artifacts with the code above, likely because it was out of context. I am sure it works where it was meant to. Anyway, here is a very streamlined implementation:
// this is the declaration of a left vertical subview of
// 'splitViewController', which is the name of the split view's outlet
var leftView: NSView {
return self.splitViewController.subviews[0] as NSView
}
// here is the action of a button that toggles the left vertical subview
// the left subview is always restored to 100 pixels here
#IBAction func someButton(sender: AnyObject) {
if splitViewController.isSubviewCollapsed(leftView) {
splitViewController.setPosition(100, ofDividerAtIndex: 0)
leftView.hidden = false
} else {
splitViewController.setPosition(0, ofDividerAtIndex: 0)
leftView.hidden = true
}
}
To see a good example using animations, control-click to download this file.
If your NSSplitView control is part of a NSSplitViewController object, then you can simply use this:
splitViewController.toggleSidebar(nil)