Is there a quicker way to detect UIScrollView's contentOffset/how does UIPickerView trigger feedback even at high velocities? - uiscrollview

I have a horizontally-scrolling UICollectionView to mimic iOS's UIPickerView. Now, I want to trigger haptic feedback when selection changes just like UIPickerView does.
The problem is that when you scroll at high speeds, scrollViewDidScroll isn't called quickly enough.
Currently, I'm checking the x offset of the collection view and triggering haptic feedback when the x offset is a multiple of the cells' width (12pt), but when the x offset jumps by more than 12, feedback is skipped.

Instead of trying to provide feedback when the offset is an exact multiple of the cell's width, provide feedback when the "current cell" changes.
Keep a "current cell" (index) class-level variable:
var currentCellIndex: Int = 0
let cellWidth: CGFloat = 12.0 // or however you get it
on scrolling, get the "new" cell:
let newCellIndex: Int = Int(scrollView.contentOffset.x / cellWidth)
if newCellIndex != currentCellIndex {
// provide feedback
currentCellIndex = newCellIndex
}
So, at start, contentOffset.x will be equal to 0, and currentCellIndex will be 0. As the user scrolls (very, very slowly), you'll get:
Int( 1.0 / 12.0) // equals 0, no change
Int( 2.0 / 12.0) // equals 0, no change
Int( 3.0 / 12.0) // equals 0, no change
Int( 4.0 / 12.0) // equals 0, no change
etc ...
Int(12.0 / 12.0) // equals 1, so newIndex != curIndex... provide feedback and update curIndex
Int(13.0 / 12.0) // equals 1, no change
Int(14.0 / 12.0) // equals 1, no change
Int(15.0 / 12.0) // equals 1, no change
Int(16.0 / 12.0) // equals 1, no change
etc ...
Int(24.0 / 12.0) // equals 2, so newIndex != curIndex... provide feedback and update curIndex
When the user scrolls quickly:
Int( 9.0 / 12.0) // equals 0, no change
Int(16.0 / 12.0) // equals 1, so newIndex != curIndex... provide feedback and update curIndex
Int(31.0 / 12.0) // equals 2, so newIndex != curIndex... provide feedback and update curIndex
Int(64.0 / 12.0) // equals 5, so newIndex != curIndex... provide feedback and update curIndex
etc ...

Related

I have a question about using turtle graphic functions and looping methods on p5.js

I have to create these two included images using the turtle function and the loop method on p5js and I am struggling I was given https://editor.p5js.org/dpapanik/sketches/_lbGWWH6N this code on p5js as a start please help, thanksenter image description here
So I've played around with some of the stuff for awhile, and I've created two functions. One that makes a single quadrant of the first problem, and one that creates a single wiggly line for the second problem. This is just a base for you to work of in this process. Here's each of the functions. Also, note that each of them takes in the turtle as a parameter:
function makeLineQuadrant(turtle) {
// this currently makes the top left corner:
let yVal = windowWidth * 0.5;
let xVal = windowWidth * 0.5;
for (let i = 0; i < 13; i++) {
// loop through the 12 lines in one quadrant
turtle.face(0); // reset for the new round
turtle.penUp();
let startLeft = i * ((windowWidth * 0.5) / 12); // decide which component on the button we should start at
let endTop = (12 - i) * ((windowWidth * 0.5) / 12); // how far down the y-axis should we go? You should write this out on paper to see how it works
turtle.goto(startLeft, yVal);
turtle.penDown();
let deg = turtle.angleTo(xVal, endTop); // what direction do I need to turn?
turtle.face(deg);
let distance = turtle.distanceTo(xVal, endTop); // how far away is it?
turtle.forward(distance);
}
}
I tried to add a few comments throughout, but if there is any step that is confusing, please add a comment.
function makeSquiggle(turtle) {
turtle.setColor(color(random(0, 255), random(0, 255), random(0, 255)));
let middleX = windowWidth * 0.5, middleY = windowHeight * 0.5;
turtle.goto(windowWidth * 0.5, windowHeight * 0.5);
// let's start moving in a random direction UNTIL our distance from the center is greater than some number X
let X = 300; // arbitrary distance from center
// some variables that can help us get some random movement for our turtle:
let turtleXvel = random(-3, 3), turtleYvel = random(-3, 3);
while (turtle.distanceTo(middleX, middleY) < X) {
turtle.face(0);
// calculate movement:
let newXmove = turtle.x + turtleXvel, newYmove = turtle.y + turtleYvel;
// direct our turtle:
turtle.face(turtle.angleTo(newXmove, newYmove));
let distance = turtle.distanceTo(newXmove, newYmove); // how far away is it?
// move our turtle
turtle.penDown();
turtle.forward(distance);
// change the velocity a little bit for a smooth curving:
turtleXvel += random(-1, 1);
turtleYvel += random(-1, 1);
}
}
Note that I'm changing the velocities instead of the position directly. This is a classic Calculus / Physics problem where the derivative gives us a smaller range, so adjusting turtleXvel and turtleYvel change the position in much less drastic ways versus:
turtle.x += random(-1, 1);
turtle.y += random(-1, 1);
You should look at the difference as well to visualize this. Beyond this is working with these structural components to finish this up!

Unity Changing GUI Colors / Slider Color / Label Color

i have a Settingsscript for a small simulation.
The BackgroundColor of the camera is black. When i have a GUI.HorizontalSlider with a Label i need to change the Slidercolor. Otherwise you can not see it well.
Can someone tell me where and how to set these Colors? "ContentColor", "BackgroundColor" are not working..
My Code:
private int planetObjectsCount = 1; // number of objects in the next scene
private int skyObjectsCount = 1; // number of objects in the next scene
private int spaceObjectsCount = 1; // number of objects in the next scene
private void OnGUI()
{
GUI.color = SetGUIColor(); // Give GUI Elements a default Color
planetObjectsCount = Mathf.RoundToInt(GUI.HorizontalSlider(GetGuiRect(Screen.width / 2, Screen.height * 2 / 8), planetObjectsCount, 1, 300)); // The Slider Element - store the value
skyObjectsCount = Mathf.RoundToInt(GUI.HorizontalSlider(GetGuiRect(Screen.width / 2, Screen.height * 4 / 8), skyObjectsCount, 1, 100)); // The Slider Element - store the value
spaceObjectsCount = Mathf.RoundToInt(GUI.HorizontalSlider(GetGuiRect(Screen.width / 2, Screen.height * 6 / 8), spaceObjectsCount, 1, 100)); // The Slider Element - store the value
GUI.Label(GetGuiRect(Screen.width / 2, Screen.height * 3 / 8), "Objects on the Planet: " + planetObjectsCount.ToString()); // The Label for the Slider
GUI.Label(GetGuiRect(Screen.width / 2, Screen.height * 5 / 8), "Objects in the Sky: " + skyObjectsCount.ToString()); // The Label for the Slider
GUI.Label(GetGuiRect(Screen.width / 2, Screen.height * 7 / 8), "Objects in Space: " + spaceObjectsCount.ToString()); // The Label for the Slider
if (GUI.Button(GetGuiRect(Screen.width * 0.85f, Screen.height / 2), "Build")) // Menu Button
{
PlayerPrefs.SetInt("planetObjectsCount", planetObjectsCount); // Store the values to the PlayerPrefs
PlayerPrefs.SetInt("skyObjectsCount", skyObjectsCount); // Store the values to the PlayerPrefs
PlayerPrefs.SetInt("spaceObjectsCount", spaceObjectsCount); // Store the values to the PlayerPrefs
LoadScene("Ingame"); // Load the simulation
}
if (GUI.Button(GetGuiRect(Screen.width * 0.15f, Screen.height / 2), "Back")) // Menu Button
{
LoadScene("MainMenu"); // Back to MainMenu
}
}
internal Rect GetGuiRect(float xPos, float yPos) // Return a Rectangle for GUI Elements
{
float rectWidth = Screen.width / 5;
float rectHeight = Screen.height / 10;
xPos -= rectWidth / 2;
yPos -= rectHeight / 2;
return new Rect(xPos, yPos, rectWidth, rectHeight);
}
internal Color SetGUIColor() // Set the GUI Color
{
return Color.cyan;
}
Not quite sure what you want.
But I believe you will fix your problem by using GUIStyle or GUISkin.
Through GUIStyle you can change a lot of properities as the Font you will use and its color. So you create it and set a GUI component to use it. At GUILabel, for instance, you can pass the GUIStyle as the third parameter:
public static void Label(Rect position, string text, GUIStyle style);
And the GUISkin is a collection of these GUIStyles divided in all the UI components, so that you can create a whole new UI style.
However, I think you should know that Unity also provides a whole UI system, that you can twitch without needing code and quite frankly is way better then using OnGUI. To exemplify, here are the links of the Unity manual teaching how to created the Slider and the Label with it.

Window positioning results in space around windows on Windows 10

I have some code that positions windows to screen quadrants. It works fine on Windows XP, 7, and 8/8.1. However, on Windows 10, there is a weird gap between windows. The extra space surrounds the window on all 4 sides. I presume it has something to do with window borders, but can't figure out how to correct the problem. Any input would be highly appreciated. The code is as follows:
// Get monitor info
HMONITOR hm = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hm, &mi);
// Set screen coordinates and dimensions of monitor's work area
DWORD x = mi.rcWork.left;
DWORD y = mi.rcWork.top;
DWORD w = mi.rcWork.right - x;
DWORD h = mi.rcWork.bottom - y;
switch (corner) {
case 0: // Left top
SetWindowPos(hWnd, HWND_TOP, x, y, w / 2, h / 2, SWP_NOZORDER);
break;
case 1: // Right top
SetWindowPos(hWnd, HWND_TOP, x + w / 2, y, w / 2, h / 2, SWP_NOZORDER);
break;
case 2: // Right bottom
SetWindowPos(hWnd, HWND_TOP, x + w / 2, y + h / 2, w / 2, h / 2, SWP_NOZORDER);
break;
case 3: // Left bottom
SetWindowPos(hWnd, HWND_TOP, x, y + h / 2, w / 2, h / 2, SWP_NOZORDER);
break;
}
I managed to correct this effect by inflating target rectangle by a calculated margin like this:
static RECT GetSystemMargin(IntPtr handle) {
HResult success = DwmGetWindowAttribute(handle, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS,
out var withMargin, Marshal.SizeOf<RECT>());
if (!success.Succeeded) {
Debug.WriteLine($"DwmGetWindowAttribute: {success.GetException()}");
return new RECT();
}
if (!GetWindowRect(handle, out var noMargin)) {
Debug.WriteLine($"GetWindowRect: {new Win32Exception()}");
return new RECT();
}
return new RECT {
left = withMargin.left - noMargin.left,
top = withMargin.top - noMargin.top,
right = noMargin.right - withMargin.right,
bottom = noMargin.bottom - withMargin.bottom,
};
}
And then doing
RECT systemMargin = GetSystemMargin(this.Handle);
targetBounds.X -= systemMargin.left;
targetBounds.Y -= systemMargin.top;
targetBounds.Width += systemMargin.left + systemMargin.right;
targetBounds.Height += systemMargin.top + systemMargin.bottom;
That worked for all windows I could test it with, except Explorer windows, which I hardcoded to exclude. If I'd do that expansion on Explorer near the screen edge, window ends up spilling a large area past it to the adjacent monitor.
The default font size of windows XP/7/8/8.1 is 100%, but in windows 10 the default is to display text and items in 125%. That affects directly all the window sizes.
Go to settings, display and you will find a scroller, move it to 100% and everything should display the same way as it did in Windows 8/7/XP

NSView within NSScrollView not sizing properly (trying to size DocumentView programmatically)

ADDITION #2
I have also tried getting the size using these lines of code for contSizeW and contSizeWO:
NSSize contSizeW = [NSScrollView contentSizeForFrameSize:self.topLevelScroller.frame.size
horizontalScrollerClass:[self.topLevelScroller.horizontalScroller class]
verticalScrollerClass:nil
borderType:self.topLevelScroller.borderType
controlSize:NSRegularControlSize
scrollerStyle:NSScrollerStyleOverlay];
NSSize contSizeWO = [NSScrollView contentSizeForFrameSize:self.topLevelScroller.frame.size
horizontalScrollerClass:nil
verticalScrollerClass:nil
borderType:self.topLevelScroller.borderType
controlSize:NSRegularControlSize
scrollerStyle:NSScrollerStyleOverlay];
These above 2 sizes, SHOULD be different as one sets horizontalScrollerClass to nil, which by apples documentation means it should assume the scroller to not be visible, and by providing a non-nil, should assume it to be showing the scroller.
In this case it is ALWAYS returning the same height regardless of what I put in for horizontalScrollerClass.
EDIT & ADDITION #1
-(void)initContentView{
self.content = [[NSView alloc] init];
[self.content setFrame:NSMakeRect(1, 1,
[[BubbleSharedPathObject sharedPath] itemsInPathList] * FILE_LIST_COLUMN_WIDTH,
self.bounds.size.height - 2)];
[self.content setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin |
NSViewMaxYMargin | NSViewHeightSizable];
[self.content setAutoresizesSubviews:YES];
[self.topLevelScroller setDocumentView:self.content];
}
ORIGINAL
I have a custom view that I am attempting to size programmatically. My NSScrollView is set to have only a horizontal scroller, and no vertical scroller. The ScrollView is also set to auto hide the scrollers. I have a routine called:
-(void)addPathComponentToEnd:(NSString *)pathComp
This routine will only add width to the custom view that holds contents and is the document view for the ScrollView. The snippet of code that is interestingly not working is:
[self.topLevelScroller setAutoresizesSubviews:NO];
[self.content setAutoresizesSubviews:NO];
NSSize contSizeW = [NSScrollView contentSizeForFrameSize:self.topLevelScroller.frame.size hasHorizontalScroller:YES hasVerticalScroller:NO borderType:self.topLevelScroller.borderType];
NSSize contSizeWO = [NSScrollView contentSizeForFrameSize:self.topLevelScroller.frame.size hasHorizontalScroller:NO hasVerticalScroller:NO borderType:self.topLevelScroller.borderType];
NSLog(#"Size With = (%d, %d), Size Without = (%d, %d)", (unsigned int)contSizeW.width, (unsigned int)contSizeW.height, (unsigned int)contSizeWO.width, (unsigned int)contSizeWO.height);
// Resize the content view to handle the added list view
if( ( [[BubbleSharedPathObject sharedPath] itemsInPathList] * FILE_LIST_COLUMN_WIDTH ) > contSizeW.width ){
[self.content setFrameSize:NSMakeSize([[BubbleSharedPathObject sharedPath] itemsInPathList] * FILE_LIST_COLUMN_WIDTH + 2, contSizeW.height)];
for(FileListTableView * table in self.tablesList){
//[table setFrame:NSMakeRect(table.fileList.tag * FILE_LIST_COLUMN_WIDTH, 0, FILE_LIST_COLUMN_WIDTH, self.content.frame.size.height)];
[table setFrameSize:NSMakeSize(FILE_LIST_COLUMN_WIDTH, contSizeW.height)];
NSLog(#"file list table height = %d", (unsigned int)table.frame.size.height);
}
NSLog(#"New Rect (x, y, w, h) = (%d, %d, %d, %d)", (unsigned int)self.content.frame.origin.x, (unsigned int)self.content.frame.origin.y, (unsigned int)self.content.frame.size.width, (unsigned int)self.content.frame.size.height);
}else{
[self.content setFrame:NSMakeRect(1, 1, [[BubbleSharedPathObject sharedPath] itemsInPathList] * FILE_LIST_COLUMN_WIDTH + 2, contSizeWO.height)];
//[self.content setFrame:NSMakeRect(1, 1, [[BubbleSharedPathObject sharedPath] itemsInPathList] * FILE_LIST_COLUMN_WIDTH, self.frame.size.height - 2)];
NSLog(#"New Rect (x, y, w, h) = (%d, %d, %d, %d)", (unsigned int)self.content.frame.origin.x, (unsigned int)self.content.frame.origin.y, (unsigned int)self.content.frame.size.width, (unsigned int)self.content.frame.size.height);
}
The output from the logging with multiple calls to the subroutine containing this code is as follows:
Size With = (501, 206), Size Without = (501, 221)
New Rect (x, y, w, h) = (1, 1, 402, 221)
Size With = (501, 206), Size Without = (501, 221)
file list table height = 206
file list table height = 206
New Rect (x, y, w, h) = (1, 0, 602, 192)
Size With = (501, 206), Size Without = (501, 221)
file list table height = 206
file list table height = 206
file list table height = 206
New Rect (x, y, w, h) = (1, 0, 802, 206)
The code:
[[BubbleSharedPathObject sharedPath] itemsInPathList]
initially only returns 1, so all subsequent calls will add 1, thus that should (and I have verified) that it returns 1, then 2, 3, 4, 5, 6 and so on.
Notice from the logging that the first time the code moves through the "if" (versus the else) that the logging shows the self.content view not taking the height value of 206 that it should, and is SOMEHOW coming up with its own value of 192. Also notice that in the call after that one (and verified all others after) actually finally DO take the proper value of 206 height.
QUESTION:
Why is the view not taking on the size that I am assigning to it? Has anyone else seen this type of behavior before, how did you fix it?
Thanks much in advance.
Based on your explanation I think I understand your routine and it is indeed odd that you get the log first with 192 and then 206 again. However, (unsigned int)self.content.frame.size.height is set by contSizeW, which again is set by the deprecated method
contentSizeForFrameSize:hasHorizontalScroller:hasVerticalScroller:borderType:
According to the Xcode 6.1.1 documentation and API reference this method is deprecated since OS X 10.7 and you should use: contentSizeForFrameSize:horizontalScrollerClass:verticalScrollerClass:borderType:controlSize:scrollerStyle: instead. It also states that for existing views you can simply use the contentSize method.
Can you, please, use either one of these methods, instead of the deprecated one and see if the problem is solved. If this doesn't help, I'm as stuck as you are.
Here is the documentation on the method. Sorry for not being able to give you an easy solution.
contentSizeForFrameSize:horizontalScrollerClass:verticalScrollerClass:borderType:controlSize:scrollerStyle:
Returns the content size calculated from the frame size and the
specified specifications.
Declaration SWIFT class func contentSizeForFrameSize(_ frameSize:
NSSize,
horizontalScrollerClass horizontalScrollerClass: AnyClass?,
verticalScrollerClass verticalScrollerClass: AnyClass?,
borderType borderType: NSBorderType,
controlSize controlSize: NSControlSize,
scrollerStyle scrollerStyle: NSScrollerStyle) -> NSSize OBJECTIVE-C
+ (NSSize)contentSizeForFrameSize:(NSSize)frameSize
horizontalScrollerClass:(Class)horizontalScrollerClass
verticalScrollerClass:(Class)verticalScrollerClass
borderType:(NSBorderType)borderType
controlSize:(NSControlSize)controlSize
scrollerStyle:(NSScrollerStyle)scrollerStyle Parameters frameSize The frame size in screen coordinates.
horizontalScrollerClass The class used as the horizontal scroller. A
value of nil specifies that no horizontal scroller is used.
verticalScrollerClass The class used as the vertical scroller. A
value of nil specifies that no horizontal scroller is used.
borderType Specifies the appearance of the style of the scroll view’s
border. See NSBorderType for a list of possible values. controlSize
The control size. The possible values are specified in NSControlSize.
NSMiniControlSize is not supported. scrollerStyle Specifies the
scroll style. See NSScrollerStyle for supported values. Return Value
The content view frame size.
Discussion For an existing scroll view, you can simply use the
contentSize method.
Import Statement import AppKit
Availability Available in OS X v10.7 and later.

Eliminating off-of-ball roll in Trackball controls (with code/fix)

Is the intent of the TrackballControl to have a "border" outside the trackball that induces roll? I personally dislike it. It is a bit discontinuous, and does't really have a lot of purpose (imho).
If not, the function getMouseProjectionOnBall can be changed similar to the following. This does two things (not necessarily "correctly"):
Normalize the radius to fill both axis
Map z values outside of the ball (ie where z was previously 0)
I find this a lot more natural, personally.
Thoughts?
this.getMouseProjectionOnBall = function(clientX, clientY) {
var xnormalized = (clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft) / (_this.screen.width / 2.0);
var ynormalized = (_this.screen.height * 0.5 + _this.screen.offsetTop - clientY) / (_this.screen.height / 2.0);
var mouseOnBall = new THREE.Vector3(
xnormalized,
ynormalized,
0.0
);
var length = mouseOnBall.length();
var ballRadius = 1.0; // As a fraction of the screen
if (length > ballRadius * 0.70710678118654752440) {
var temp = ballRadius / 1.41421356237309504880;
mouseOnBall.z = temp * temp / length;
// Remove old method.
// This Left z = 0, which meant rotation axis
// becomes z, which is a roll
//mouseOnBall.normalize();
} else {
mouseOnBall.z = Math.sqrt(1.0 - length * length);
}
_eye.copy(_this.object.position).sub(_this.target);
var projection = _this.object.up.clone().setLength(mouseOnBall.y);
projection.add(_this.object.up.clone().cross(_eye).setLength(mouseOnBall.x));
projection.add(_eye.setLength(mouseOnBall.z));
return projection;
};

Resources