Storing UIView with children - swift

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.

Related

Access a dynamically created view by its tag id

Is there a way to have a view created in a for loop accessed by its tag id? If not is there a better way to achieve the below? My guess is that because I am overwriting the 'myView' var that it will always be the last value of the for loop. But is there a way to achieve something like this below? [Swift]
var myView = UIView();
// Create a for loop of 20 Views on different x/y axis
var myView = UIView();
var leftPos : CGFloat = 0;
for (var i = 0; i < 20; i++){
myView.backgroundColor = UIColor.clearColor()
myView.tag = i;
myView.frame = CGRect(x: leftPos, y: 0, width: 20, height: 20)
mySuperView.addSubview(myView)
leftPos += 20;
}
// Then later on reference it by its tag id:
myView.tag[13].backgroundColor = UIColor.redColor()
Rather than using tags, I suggest a better solution is to keep an array of your views that you can reference by index instead.
// Declare a variable to hold on to your subviews
var views = [UIView]()
// Create 20 views, add them as subviews and add them to the views array
for i in 0..<20 {
let leftPos = CGFloat(i) * 20
let view = UIView(frame: CGRect(x: leftPos, y: 0, width: 20, height: 20))
mySuperView.addSubview(view)
views.append(view)
}
// Access the view by it's index - no need for a tag.
views[13].backgroundColor = .redColor()
You can find a view with a specific tag using the viewWithTag function:
view = mySuperView.viewWithTag(13)
Note that using a tag of 0 is "bad" since that's the default tag value if no other value is used.

CCScrollview not working

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 ?

Custom TextField layer / background / adapt using swift

I would like to know if it's possible to create a custom background for a textfield using swift, for example expecting 4 letters, the textfield has 4 square. thx
Do you mean like this?
// Background
txtField.backgroundColor = UIColor.greenColor()
// Placeholder
txtField.placeholder = "1"
// Corner radius
txtField.layer.cornerRadius = 8
// A Boolean indicating whether sublayers are clipped to the layer’s bounds
txtField.layer.masksToBounds = true
UPDATE
Here is a method that will randomize a number and then create textfields according to the number that has been generated. You don´t need the random functionality of course if you already have a number. The only thing you have to do is change for var i = 0; i < rand; i++ rand to your variable. The textFieldEditingChanged function is the event when the users tries to add a new character to the textField and there is a check to only allow one character in each textField.
let rand = Int(arc4random_uniform(10) + 2)
for var i = 0; i < rand; i++ {
let myField: UITextField = UITextField (frame:CGRectMake(CGFloat(i * 30), 50, 25, 25));
myField.text = String(i)
myField.backgroundColor = UIColor.redColor()
myField.addTarget(self, action: "textFieldEditingChanged:", forControlEvents: UIControlEvents.EditingChanged)
self.view.addSubview(myField)
}
If you only have a String and you want to populate each char in a textField, you could do like this:
let word = "Car"
for var i = 0; i < word.characters.count; i++ {
let myField: UITextField = UITextField (frame:CGRectMake(CGFloat(i * 30), 50, 25, 25));
myField.text = String(word[word.startIndex.advancedBy(i)])
myField.backgroundColor = UIColor.redColor()
myField.addTarget(self, action: "textFieldEditingChanged:", forControlEvents: UIControlEvents.EditingChanged)
self.view.addSubview(myField)
}
Click event for the textField
func textFieldEditingChanged(target: UITextField){
if (target.text?.characters.count > 1){
target.text = target.text?.substringToIndex(target.text!.endIndex.predecessor())
}
}
UPDATE
To change responder
Declare a variable var tagNumber = 0 and when you loop through in your for statement and create your textFields just add this row to myField.tag = i. In your textFieldEditingChanged function, add this code block:
if (target.tag < tagNumber){
let nextTag: NSInteger = target.tag + 1;
// Try to find next responder
if let nextResponder: UIResponder! =
target.superview!.viewWithTag(nextTag){
nextResponder.becomeFirstResponder()
}
}

Keeping an image on screen in Swift

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.

UIScrollView with "Circular" scrolling

I am trying to make "Circular" scrolling in my UIScrollView, but unsuccessful.
What I want to do:
if uiscrollview reaches end, it should move to start
if uiscrollview at start and moving back, it should move to end
Appending scrollview isn't good way in my situation (other methods should get "page id")
Have you any ideas?
I've implemented this method, but it requires paging enabled. Lets assume you have five elements A,B,C,D and E. When you set up your view, you add the last element to the beginning and the first element to the end, and adjust the content offset to view the first element, like this E,[A],B,C,D,E,A. In the UIScrollViewDelegate, check if the user reach any of the ends, and move the offset without animation to the other end.
Imagine the [ ] indicates the view being shown:
E,A,B,C,[D],E,A
User swipes right
E,A,B,C,D,[E],A
User swipes right
E,A,B,C,D,E,[A]
Then, automatically set the content offset to the second element
E,[A],B,C,D,E,A
This way the user can swipe both ways creating the illusion of an infinite scroll.
E,A,[B],C,D,E,A
Update
I've uploaded a complete implementation of this algorithm. It's a very complicated class, because it also has on-click selection, infinite circular scroll and cell reuse. You can use the code as is, modify it or extract the code that you need. The most interesting code is in the class TCHorizontalSelectorView.
Link to the file
Enjoy it!
Update 2
UICollectionView is now the recommended way to achieve this and it can be used to obtain the very same behavior. This tutorial describes in details how to achieve it.
Apple has a Street Scroller demo that appears to have exactly what you want.
There's also a video from WWDC 2011 that demos their demo. ;) They cover infinite scrolling first in the video.
Try to use following code..
Sample code..
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
if (scrollView.contentOffset.x == 0) {
// user is scrolling to the left from image 1 to image n(last image).
// reposition offset to show image 10 that is on the right in the scroll view
[scrollView scrollRectToVisible:CGRectMake(4000,0,320,480) animated:NO];// you can define your `contensize` for scrollview
}
else if (scrollView.contentOffset.x == 4320) {
// user is scrolling to the right from image n(last image) to image 1.
// reposition offset to show image 1 that is on the left in the scroll view
[scrollView scrollRectToVisible:CGRectMake(320,0,320,480) animated:NO];
}
}
Hope, this will help you...
With reference from #redent84, find the code logic.
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(imagesArray.count), height: scrollView.frame.height)
scrollView.isPagingEnabled = true
// imagesArray.insert(imagesArray.last as? UIImage, at: 0)
// imagesArray.insert(imagesArray.first as? UIImage, at: imagesArray.count - 1)
var testArr = ["A", "B", "C", "D"]
let firstItem = testArr.first
let lastItem = testArr.last
testArr.insert(lastItem as! String, at: 0)
testArr.append(firstItem as! String)
print(testArr)
for i in 0..<imagesArray.count{
let imageview = UIImageView()
imageview.image = imagesArray[i]
imageview.contentMode = .scaleAspectFit
imageview.clipsToBounds = true
let xPosition = self.scrollView.frame.width * CGFloat(i)
imageview.frame = CGRect(x: xPosition, y: 0, width: self.scrollView.frame.width, height: self.scrollView.frame.height)
print(imageview)
scrollView.addSubview(imageview)
print("Imageview frames of i \(i) is \(imageview.frame)")
}
newStartScrolling()
}
func newStartScrolling()
{
ViewController.i += 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
print("X is \(x) and i is \(ViewController.i)")
if ViewController.i == imagesArray.count - 1 {
ViewController.i = 0
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
//scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: false)
print("X (rotate) is \(x) and i is \(ViewController.i)")
scrollView.contentOffset.x = x
self.newStartScrolling()
}
}
func backScrolling() {
ViewController.i -= 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
print("X is \(x) and i is \(ViewController.i)")
if ViewController.i <= 0 {
ViewController.i = imagesArray.count - 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: false)
print("X (rotate) is \(x) and i is \(ViewController.i)")
self.backScrolling()
//scrollView.contentOffset.x = x
return
}
}