I am using Sprite Builder and created a scrollview to create selection of players list in stripe which can be scrolled horizontally.
PlayerListContainer.ccb:Scrollview Container
Taken a scroll view of width: 300, height: 320 with Horizontal scrolling enabled and Bounces enabled.
Select Content node as PlayerList.ccb (Thus needs to create another PlayerList.ccb class)
PlayerListContainer.swift - noting major but just call back functions are called.
PlayerList.ccb : Main listing
- Take CCNode with width, height as 300,320
PlayerList.swift
//
// PlayerList.swift
// tsb
//
// Created by Paresh Thakor on 27/12/15.
// Copyright (c) 2015 Apportable. All rights reserved.
//
import Foundation
//let Pi = CGFloat(M_PI)
//let degreesToRadians = CGFloat(Pi / 180)
//let radiansToDegrees = CGFloat(180 / Pi)
class PlayerList: CCNode {
//weak var __horizontalLayout: CCLayoutBox!
//weak var __container: CCScrollView!
weak var __node: CCNode!
var userDefaults = NSUserDefaults.standardUserDefaults()
override init!() {
super.init()
NSLog("init Player list")
}
func didLoadFromCCB() {
NSLog("Player list loaded")
__node.contentSizeType = CCSizeTypeNormalized
//__node.contentSizeInPoints = CGSizeMake(1000, 500)
__node.contentSize = CGSizeMake(3, 1)
// Add player children to select
for (var i = 0; i<20; i++) {
let playerBox = CCNodeColor(color: CCColor.redColor())
//playerBox.contentSize = CGSizeMake(50, 50)
let player = CCSprite(imageNamed: "ccbResources/playerBall.png")
playerBox.contentSize = CGSizeMake(player.contentSizeInPoints.width+20, player.contentSizeInPoints.height+20)
playerBox.position = ccp(CGFloat(5)+((playerBox.contentSizeInPoints.width+CGFloat(5)) * CGFloat(i)),5)
player.position = ccp(playerBox.contentSizeInPoints.width/2, playerBox.contentSizeInPoints.height/2)
playerBox.addChild(player)
__node.addChild(playerBox)
}
//self.contentSizeInPoints = CGSizeMake(20*50, 50)
self.userInteractionEnabled = true
self.color = CCColor(UIColor: UIColor.greenColor())
}
}
This works but the scrollview is not being displayed. this will load infinite boxes which we placed.
This scrollview does not scroll to right, etc. nothing happens.
Can some one tell me ?
Related
I place 3d object in the world space. After that I try to move camera randomly. Then right now I need to know after I knew object has became inside frustum by isNode method, if the object is in center, top or bottom of camera view.
For a solution that's not a hack you can use the projectPoint: API.
It's probably better to work with pixel coordinates because this method uses the actual camera's settings to determine where the object appears on screen.
let projectedPoint = sceneView.projectPoint(self.sphereNode.worldPosition)
let xOffset = projectedPoint.x - screenCenter.x;
let yOffset = projectedPoint.y - screenCenter.y;
if xOffset * xOffset + yOffset * yOffset < R_squared {
// inside a disc of radius 'R' at the center of the screen
}
Solution
To achieve this you need to use a trick. Create new SCNCamera, make it a child of pointOfView default camera and set its FoV to approximately 10 degrees.
Then inside renderer(_:updateAtTime:) instance method use isNode(:insideFrustumOf:) method.
Here's working code:
import ARKit
class ViewController: UIViewController,
ARSCNViewDelegate,
SCNSceneRendererDelegate {
#IBOutlet var sceneView: ARSCNView!
#IBOutlet var label: UILabel!
let cameraNode = SCNNode()
let sphereNode = SCNNode()
let config = ARWorldTrackingConfiguration()
public func renderer(_ renderer: SCNSceneRenderer,
updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
if self.sceneView.isNode(self.sphereNode,
insideFrustumOf: self.cameraNode) {
self.label.text = "In the center..."
} else {
self.label.text = "Out OF CENTER"
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.allowsCameraControl = true
let scene = SCNScene()
sceneView.scene = scene
cameraNode.camera = SCNCamera()
cameraNode.camera?.fieldOfView = 10
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.sceneView.pointOfView!.addChildNode(self.cameraNode)
}
sphereNode.geometry = SCNSphere(radius: 0.05)
sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
sphereNode.position.z = -1.0
sceneView.scene.rootNode.addChildNode(sphereNode)
sceneView.session.run(config)
}
}
Also, in this solution you may turn on an orthographic projection for child camera, instead of perspective one. It helps when a model is far from the camera.
cameraNode.camera?.usesOrthographicProjection = true
Here's how your screen might look like:
Next steps
The same way you can append two additional SCNCameras, place them above and below central SCNCamera, and test your object with two extra isNode(:insideFrustumOf:) instance methods.
I solved problem with another way:
let results = self.sceneView.hitTest(screenCenter!, options: [SCNHitTestOption.rootNode: parentnode])
where parentnode is the parent of target node, because I have multiple nodes.
func nodeInCenter() -> SCNNode? {
let x = (Int(sceneView.projectPoint(sceneView.pointOfView!.worldPosition).x - sceneView.projectPoint(sphereNode.worldPosition).x) ^^ 2) < 9
let y = (Int(sceneView.projectPoint(sceneView.pointOfView!.worldPosition).y - sceneView.projectPoint(sphereNode.worldPosition).y) ^^ 2) < 9
if x && y {
return node
}
return nil
}
I want to animate two textviews, TV1 is on screen and TV2 is offscreen. When a user clicks a button TV1 should reduce its size to 1/2 and TV2 should move from offscreen to lower half of the parent view.
I have this code but only the first view is being animated:
func toggleFrames() {
var frame1H = self.view.frame.size.height
var frame2H = CGFloat(0.0)
var frame2Y = CGFloat(0.0)
var txtV1Frame = txtContents.frame
var txtV2Frame = txtChanges.frame
if bViewingChanges {
frame1H = frame1H/2
frame2Y = (self.view.frame.size.height/2) - 10.0
frame2H = frame1H
}
txtV1Frame.size.height = frame1H
txtV2Frame.size.height = frame2H
txtV2Frame.origin.y = 550.0
NSAnimationContext.runAnimationGroup({_ in
NSAnimationContext.current.duration = 0.5
txtContents.animator().frame = txtV1Frame //reduce size of text view 1
txtChanges.animator().frame = txtV2Frame //<--Grumble, not doing anything
}, completionHandler:{
print("Done animation")
})
}
I am simply trying to change the background color of the last element of a CALayer array. Here is my entire View Class, however its only 2-3 lines that I actually try to access the last element of the CALayer.
Here is my progressViewClass and I put comments to where exactly my problem is:
class ProgressBarView: UIView {
//Variables for progress bar
var holdGesture = UILongPressGestureRecognizer()
let animation = CABasicAnimation(keyPath: "bounds.size.width")
var layerHolder = [CALayer]()
var widthIndex = CGPoint(x: 0, y: 0)
var nextXOffset = CGFloat(0.0)
var checkIfFull = CGFloat()
var newLayer : CALayer?
var progressBarAnimationDuration : CFTimeInterval = (MainController.sharedInstance.totalMiliSeconsToRecord / 10)
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
}
func startProgressBar(){
if(RecordingViewController().currentCameraMode == .recordingMode || RecordingViewController().currentCameraMode == .record1stClipMode) {
newLayer = CALayer()
newLayer?.frame = CGRect(x: nextXOffset, y: 0, width: 0, height: self.bounds.height)
newLayer?.backgroundColor = UIColor(red:0.82, green:0.01, blue:0.11, alpha:1.0).cgColor
//print("before \(nextXOffset)")
newLayer?.anchorPoint = widthIndex
animation.fromValue = 0
animation.toValue = self.bounds.width - nextXOffset
animation.duration = progressBarAnimationDuration - ((MainController.sharedInstance.miliSecondsPassed) / 10)
self.layer.addSublayer(newLayer!)
//print("Long Press Began")
newLayer?.add(animation, forKey: "bounds.size.width")
}
else{
stopProgressBar()
}
}
func stopProgressBar(){
if(RecordingViewController().currentCameraMode != .recordingMode){
pauseLayer(layer: newLayer!)
newLayer?.frame = (newLayer?.presentation()!.frame)!
nextXOffset = (newLayer?.frame.maxX)!
layerHolder.append(newLayer!)
print("Layerholder has elements : \(layerHolder.count)")
}
}
// HERE IS MY PROBLEM
func highlightLastLayer(){
print("in highlight last layer Layerholder has elements : \(layerHolder.count)")
// I CAN HIDE THE CALAYER SO I BELIEVE IM ACCESSING THE CORRECT LAYER
// layerHolder.last?.isHidden = true
// This is suppose to change the last element background color to blue but doesnt
layerHolder.last?.backgroundColor = UIColor.blue.cgColor
}
// ALSO MY PROBLEM
func unhighlightLastLayer(){
print("inside unhighlight last layer")
// I CAN HIDE THE CALAYER SO I BELIEVE IM ACCESSING THE CORRECT LAYER
//layerHolder.last?.isHidden = false
// Changes CALayer back to red
layerHolder.last?.backgroundColor = UIColor(red:0.82, green:0.01, blue:0.11, alpha:1.0).cgColor
}
//Function to pause the Progress Bar
func pauseLayer(layer : CALayer){
let pausedTime : CFTimeInterval = layer.convertTime(CACurrentMediaTime(), from: nil)
layer.speed = 0.0
layer.timeOffset = pausedTime
}
}
Simply put, I create a progressView object in my viewController and then call those functions based on certain button input. This view is essentially a progress bar that you'd see in many video recording applications to show how much you have recorded. In the highlightLastLayer, I am trying to grab the last element of the "layerHolder" array and change its color to blue. Simple right? Doesn't work. Any ideas?
Where are you calling highlight and unhighlight. I am pretty sure you are doing this when you are "stopped" or "paused" because thats the only time you add anything to the layerHolder Array. You can only do this when you are not animating because when you are animating the presentation layer is shown instead of the "real" layer. Instead of setting the speed to zero, setup your layer to look like the current animation state and call layer. removeAllAnimations to kill the presentation layer and show the actual layer for your view instead. Now you can make all the changes that you want and they will actually show up.
Im working on a 2x2 grid with everyone of them having a UIView and a child label with text and background color.
I generate the UIView with a for loop like this:
// Generatin answer cube buttons
for var i = 0; i < cubeCount; i++
{
// two answers case
if(cubeCount < 3)
{
var btn = button(xpos, y: ypos, width: screenWidth, height: (screenHeight * 2));
var lbl = labelis("\(i)", btn: btn)
btn.addSubview(lbl)
xpos = xpos + (screenWidth + 10);
self.view.addSubview(btn);
}
// 3+ answers case
else
{
var btn = button(xpos, y: ypos, width: screenWidth, height: screenHeight);
var lbl = labelis("\(i)", btn: btn)
btn.addSubview(lbl)
xpos = xpos + (screenWidth + 10)
self.view.addSubview(btn)
// change row in case of more than 2 answers
if(i == 1)
{
xpos = 20
ypos = ypos + (screenHeight + 10)
}
}
I also have a tapGesture function letting me know when I click on one of the answer cube.
My problem here is that, when clicking on one of the cube, I would like to access all the cubes and change their label's background color.
I though about storing the UIView into an array so I could act on them from the tapGesture function, but I don't see how I could make this work.
I though that maybe someone could guide me through the way of dealing with this.
thanks.
In the click handler of cube button try following code:
for subview in view.subviews as [UIView] {
if let cubeButton = subview as? button {
//Do something with this cubeButton here.
}
}
So I've found a solution to my problem so I answer my own question for people who might look into this in the future:
I've created an array:
var answerData:[UIView] = [];
and added my UIView along the way for those I wanted to keep stored
var uiviewvariable = UIView()
answerData.append(uiviewVariable)
then, on my tapGesture function, when the user tap one of the view, I can call for the array and use act on the UIViews
// Accessing stored UIView
for(var i = 0; i < answerData.count; i++){
// Accessing subviews
for subview in answerData[i].subviews as [UIView]
{
if subview.tag == 0
{
if let label = subview as? UILabel {
// do something with label inside the views
}
}
}
}
for some reason I use something similar than Abdullah's answer for selecting my labels but I didn't managed to make his snippet work.
I have a game where I use a UISwipeGestureRecognizer to move the screen. I am trying to figure out how to make it so that the image that I am swiping cannot move off the screen. I have looked around the internet and have not been able to find anything.
Here is my swipe method:
func swipedRight(sender: UISwipeGestureRecognizer) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("moveBackground"), userInfo: nil, repeats: true)
if imageView.frame.origin.x > 5000 {
imageView.removeFromSuperview()
}
}
Here is my moveBackground method:
func moveBackground() {
self.imageView.frame = CGRect(x: imageView.frame.origin.x - 100, y: imageView.frame.origin.y, width: 5500, height: UIScreen.mainScreen().bounds.height)
}
The only things I have tried so far is to check if the view is in a certain position and remove it if it is but that hasn't worked. I added that code to the swipe method.
It will be very helpful if you post your class code here, so we can be more specific helping you.
By the way, you should check the object position each time the function is called, before move it, taking in mind its size.
Lets say you have a playing cards game and you are swiping a card along the board.
To avoid swiping the card off the board (off the screen) you will have to do something like this:
// I declare the node for the card with an image
var card = SKSpriteNode(imageNamed: "card")
// I take its size from the image size itself
card.physicsBody = SKPhysicsBody(rectangleOfSize: card.size)
// I set its initial position to the middle of the screen and add it to the scene
card.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2)
self.addChild(card)
// I set the min and max positions for the card in both X and Y axis regarding the whole scene size.
// Fit it to your own bounds if different
let minX = card.size.width/2
let minY = card.size.height/2
let maxX = self.frame.size.width - card.size.height/2
let maxY = self.frame.size.width - card.size.height/2
// HERE GOES YOUR SWIPE FUNCTION
func swipedRight(sender: UISwipeGestureRecognizer) {
// First we check the current position for the card
if (card.position.x <= minX || card.position.x >= maxX || card.position.y <= minY || card.position.y >= maxY) {
return
}
else {
// ...
// YOUR CODE TO MOVE THE ELEMENT
// ...
}
}
I hope this helps!
Regards.