UICollectionView: Custom UI for a manually selected Cell - cell

I'm overriding (BOOL)isSelected {} from UICollectionViewCell to change the cells appearance on selection. This does work as intend if I'm clicking on a cell. But the isSelected method never gets called if I'm setting the selection manually. Is there an elegant way to resolve this?
Overriden method in my custom cell:
(BOOL)isSelected {
if ([super isSelected]) {
self.contentView.alpha = 0.2;
self.contentView.backgroundColor = [UIColor greenColor];
return YES;
} else {
self.contentView.alpha = 1.0;
self.contentView.backgroundColor = [UIColor clearColor];
return NO;
}
}
I want to manually select an cell in my controller like this:
[self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];

My mistake was to override the method isSelected.
I need to override the setter method setSelected everything works as expected.
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
self.contentView.alpha = 0.2;
self.contentView.backgroundColor = [UIColor greenColor];
} else {
self.contentView.alpha = 1.0;
self.contentView.backgroundColor = [UIColor clearColor];
}
}

Related

How to create top Navigation like Reddit App in Swift 3

Here is the UI i want to create:
and what the complete UI looks like:
but i want to create first top Navigation. i tried https://github.com/subinspathilettu/SJSegmentedViewController
Which is working but it shows navigation after that it shows SegmentView.Could someone help me to create this UI it's same like reddit app screen after login.I need in swift.Thanks in advance
Okay. Here is the solution.
Step 1:
Take no. of buttons you want in a UIView.
Take UILabel for equal width of each button and height should be 1.0 and put it under each button.
Now give Outlets to each buttons and their respected labels in your ViewController.
Now take UIScrollView under this UIView and set constraints and give outlet.
Now you have to create ViewControllers for each Tab you want in Too Navigation.
For Example:-
I have a ProfileViewController.m like below.
#interface ProfileViewController ()<UIScrollViewDelegate>
#property (nonatomic,strong) AboutMeViewController *aboutMeVC;
#property (nonatomic,strong) AddressViewController *addressVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_btnAboutMe.selected = NO;
_btnAddress.selected = YES;
_btnPassword.selected = NO;
[self topButtonTapped:_btnAddress]; // This will call the navigation button tap method.
_controllerArray = [#[self.aboutMeVC,self.addressVC,[self.storyboard instantiateViewControllerWithIdentifier:#"changePasswordVC"]] mutableCopy];
[self addChildViewControllersOntoContainer:_controllerArray];
//self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Left" style:UIBarButtonItemStylePlain target:self action:#selector(presentLeftMenuViewController:)];
[_leftBarButton setTarget:self];
[_leftBarButton setAction:#selector(presentLeftMenuViewController:)];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(profileTap:)];
tapGesture.numberOfTapsRequired = 1;
[_profilePicture addGestureRecognizer:tapGesture];
}
-(AboutMeViewController*)aboutMeVC
{
if (!_aboutMeVC) {
_aboutMeVC = [self.storyboard instantiateViewControllerWithIdentifier:#"aboutMeVC"];
}
_aboutMeVC.delegate = self;
return _aboutMeVC;
}
-(AddressViewController*)addressVC
{
if (!_addressVC) {
_addressVC = [self.storyboard instantiateViewControllerWithIdentifier:#"addressVC"];
}
return _addressVC;
}
// Adding all related controllers into the container
-(void) addChildViewControllersOntoContainer:(NSArray *)controllersArr
{
for (int i = 0; i < controllersArr.count; i++)
{
UIViewController *vc = (UIViewController *)[controllersArr objectAtIndex:i];
CGRect frame = CGRectMake(0, 0, self.containerScrollView.frame.size.width, self.containerScrollView.frame.size.height);
frame.origin.x = SCREEN_WIDTH * i;
vc.view.frame = frame;
[self addChildViewController:vc];
[self.containerScrollView addSubview:vc.view];
[vc didMoveToParentViewController:self];
}
self.containerScrollView.contentSize = CGSizeMake(SCREEN_WIDTH * controllersArr.count + 1, self.containerScrollView.frame.size.height - 99);
self.containerScrollView.pagingEnabled = YES;
self.containerScrollView.delegate = self;
}
// Scroll view delegate methods.
// This method will help you to change the navigation by sliding it.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
NSInteger page = (scrollView.contentOffset.x / SCREEN_WIDTH);
[self changeButtonState:page];
//_selectedMenu = page;
//_segmentControl.selectedSegmentIndex = page;
NSLog(#"%#", NSStringFromCGSize(scrollView.contentSize));
// float xx = scrollView.contentOffset.x * (buttonWidth / SCREEN_WIDTH) - buttonWidth;
// [self.menuScrollView scrollRectToVisible:CGRectMake(xx, 0, SCREEN_WIDTH, self.menuScrollView.frame.size.height) animated:YES];
}
- (IBAction)topButtonTapped:(UIButton *)sender // Here is the method when you click on top button of your navigation bar.
{
[self changeButtonState:sender.tag];
[self.containerScrollView setContentOffset:CGPointMake(SCREEN_WIDTH * sender.tag, 0) animated:YES];
}
-(void)changeButtonState:(NSInteger)tag
{
if (tag == 0)
{
_btnAboutMe.selected = YES;
_btnAddress.selected = NO;
_btnPassword.selected = NO;
_btnAboutMe.fillColor = [GeneralClass getColorFromHex:kBlueColor withAlpho:1.0];
_btnAddress.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
_btnPassword.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
}
else if (tag == 1)
{
_btnAboutMe.selected = NO;
_btnAddress.selected = YES;
_btnPassword.selected = NO;
_btnAboutMe.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
_btnAddress.fillColor = [GeneralClass getColorFromHex:kBlueColor withAlpho:1.0];
_btnPassword.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
}
else if (tag == 2)
{
_btnAboutMe.selected = NO;
_btnAddress.selected = NO;
_btnPassword.selected = YES;
_btnAboutMe.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
_btnAddress.fillColor = [GeneralClass getColorFromHex:kWhiteColor withAlpho:1.0];
_btnPassword.fillColor = [GeneralClass getColorFromHex:kBlueColor withAlpho:1.0];
}
}
Your ViewController may look like this in your Storyboard.
I hope this will help you or at least give you some idea to develop what you need. Sorry for my answer in Objective C and some messy code. Actually I am in a hurry. But this will help you for sure. Thanks! :)
Note:- Please follow steps closely. You will find your solution for sure. Thanks. :)

How to increase gravity using an if statement

I have set my gravity so that at the start of my game the objects fall quite slow and as the player collects these items i want the gravity to increase.
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -3.0)
initializeGame()
}
I have set the gravity like so but i am unsure on how to edit it with an if statement. E.g. if score = 20 gravity increases. any help would be great on how to properly do this
You might want to look through a SpriteKit tutorial. Generally, updating properties happens in the update(_ currentTime: TimeInterval) method. You can implement this method in your SKScene subclass:
override func update(_ currentTime: TimeInterval) {
if (score == 20) { // whatever condition you want
// whatever you want to do
}
}
if (score == 20) {
self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -8.0)
}
Change value of 'dy' as per your requirements

Swift 3.0 - Sprite Kit - Multitouch

I'm new to Swift SpriteKit, I want to make a game like a virtual joystick and two buttons(two nodes), I've enabled the multi-touch. However, whenever I move both virtual joystick and attack Spritenode, the virtual joystick of the button seems to be Jagging. How am I gonna separate the touches of virtual joystick from touches attackbutton
class GameScene: SKScene {
var defend : Bool = false
var attack : Bool = false
var stickMove : Bool = false
var stickEnd:Bool = false
var moveattack:Bool = false
var movedefend:Bool = false
var playermovement:Bool = true
let vj1 = SKSpriteNode(imageNamed: "vj1")
let vj2 = SKSpriteNode(imageNamed: "vj2")
let player = SKSpriteNode(imageNamed: "player")
let rotationSpeed :CGFloat = CGFloat(M_PI)
let rotationOffSet : CGFloat = -CGFloat(M_PI/2.0)
let attackvj = SKSpriteNode(imageNamed: "attackvj")
let defendvj = SKSpriteNode(imageNamed: "defendvj")
private var touchPosition: CGFloat = 0
private var targetZRotation: CGFloat = 0
override func didMove(to view: SKView) {
self.view?.isMultipleTouchEnabled = true
self.backgroundColor = SKColor.black
//position of joystick
vj1.zPosition = 1
vj1.xScale = 1.5
vj1.yScale = 1.5
self.addChild(vj1)
vj1.position = CGPoint(x: self.size.width*15/100, y:self.size.height*30/100)
vj2.zPosition = 1
vj2.xScale = 1.5
vj2.yScale = 1.5
self.addChild(vj2)
vj2.position = vj1.position
player.zPosition = 0
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody!.affectedByGravity = false
player.position = CGPoint(x: self.size.width/2, y:self.size.height/2)
self.addChild(player)
attackvj.anchorPoint = CGPoint(x: 0.5, y:0.5)
attackvj.position = CGPoint(x: self.size.width*80/100, y:self.size.height*30/100)
attackvj.xScale = 2.0
attackvj.yScale = 2.0
self.addChild(attackvj)
defendvj.anchorPoint = CGPoint(x: 0.5, y:0.5)
defendvj.position = CGPoint(x: self.size.width*90/100, y:self.size.height*50/100)
defendvj.xScale = 2.0
defendvj.yScale = 2.0
self.addChild(defendvj)
vj1.alpha = 0.4
vj2.alpha = 0.4
attackvj.alpha = 0.4
defendvj.alpha = 0.4
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches){
let location = touch.location(in: self)
if vj2.contains(location){
stickEnd = false
stickMove = true
}
if defendvj.contains(location){
defend = true
}
if attackvj.contains(location){
attack = true
attackvj.xScale = 2.5
attackvj.yScale = 2.5
}
if(stickMove == true && attack == true){
moveattack = true
}
if(stickMove == true && defend == true){
movedefend = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches){
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
let v = CGVector(dx: location.x - vj1.position.x, dy: location.y - vj1.position.y)
print("locationsss" , location , "previouslocationsss", previousLocation)
let angle = atan2(v.dy, v.dx)
targetZRotation = angle + rotationOffSet
let length:CGFloat = vj1.frame.size.height / 2
let xDist:CGFloat = sin(angle - 1.57079633) * length
let yDist:CGFloat = cos(angle - 1.57079633) * length
if(stickMove == true){
if(vj1.frame.contains(location)){
vj2.position = location
}
else{
vj2.position = CGPoint(x: vj1.position.x - xDist, y: vj1.position.y + yDist)
}
if(attackvj.frame.contains(location)){//How am I gonna make this location in attackvj, not to influence my joystick location?
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(stickMove == true && attack == false && defend == false){
let move:SKAction = SKAction.move(to: vj1.position, duration: 0.2)
move.timingMode = .easeOut
vj2.run(move)
stickEnd = true
stickMove = false
}
if(attack == true){
attack = false
attackvj.xScale = 2.0
attackvj.yScale = 2.0
moveattack = false
}
if(defend == true){
defend = false
movedefend = false
}
}
override func update(_ currentTime: TimeInterval) {
//rotation
if (stickEnd == false) {
var angularDisplacement = targetZRotation - player.zRotation
if angularDisplacement > CGFloat(M_PI) {
angularDisplacement = (angularDisplacement - CGFloat(M_PI)*2)
}
else if angularDisplacement < -CGFloat(M_PI) {
angularDisplacement = (angularDisplacement + CGFloat(M_PI)*2)
}
if abs(angularDisplacement) > rotationSpeed*(1.0/60.0){
let angularVelocity = angularDisplacement < 0 ? -rotationSpeed : rotationSpeed
player.physicsBody!.angularVelocity = angularVelocity
} else {
player.physicsBody!.angularVelocity = 0
player.zPosition = targetZRotation
}
}
else{
player.physicsBody!.angularVelocity = 0
}
//movement but use attack button to testing
if (attack == true)
{
player.position = CGPoint(x:player.position.x + cos(player.zRotation + 1.57079633),y:player.position.y + sin(player.zRotation + 1.57079633))
}
}
The problem you are facing is that you are mixing the contexts for your touches. This is making things more difficult and complicated than they need to be.
The easiest thing to do would be to make your virtual joystick a separate SKSpriteNode class that tracks its own touches and reports them. Same with the buttons - they track their own touches and report their state.
But if you want to continue with your current approach of having a high-level object track multiple touches, what you want to do is capture the context that each touch is associated with in touchesBegan, and then just update things on touchesMoved as necessary, canceling the touches in touchesEnded.
For instance, you want to associate a particular touch with the virtual joystick, because you don't want weirdness if they drag their finger off of it and over to the button, say. And you want to know exactly which touch is lifted off when the user lifts a finger.
Here's some sample code that should illustrate the process:
//
// This scene lets the user drag a red and a blue box
// around the scene. In the .sks file (or in the didMove
// function), add two sprites and name them "red" and "blue".
//
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var redTouch:UITouch?
private var blueTouch:UITouch?
override func didMove(to view: SKView) {
super.didMove(to: view)
isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Grab some references to the red and blue sprites.
// (They must be direct children of the scene, or the
// paths must be amended.)
guard let redBox = childNode(withName: "red") else { return }
guard let blueBox = childNode(withName: "blue") else { return }
for touch in touches {
// Get the location of the touch in SpriteKit Scene space.
let touchLocation = touch.location(in: self)
// Check to see if the user is touching one of the boxes.
if redBox.contains( touchLocation ) {
// If we already have a touch in the red box, do nothing.
// Otherwise, make this our new red touch.
redTouch = touch
} else if blueBox.contains( touchLocation ) {
// If we already have a touch in the blue box, do nothing.
// Otherwise, make this our new blue touch.
blueTouch = touch
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// We have already established which touches are active,
// and we have already tied them to the two contexts, so
// we just need to read their current location and update
// the location of the red and blue boxes for the touches
// that are active.
if let redTouch = redTouch {
guard let redBox = childNode(withName: "red") else { return }
let location = redTouch.location(in:self)
redBox.position = location
}
if let blueTouch = blueTouch {
guard let blueBox = childNode(withName: "blue") else { return }
let location = blueTouch.location(in:self)
blueBox.position = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// The parameter touches contains a list of ending touches,
// so we check the touches we are currently tracking to
// see if they are newly lifted. If so, we cancel them.
if let touch = redTouch {
if touches.contains( touch ) {
redTouch = nil
}
}
if let touch = blueTouch {
if touches.contains( touch ) {
blueTouch = nil
}
}
}
}
In the above code, we have separated out the touches on the red box and the blue box. We always know which touch is dragging the red box around and which touch is dragging the blue box around, if any. This is a simple example, but it's generalizable to your situation, where you'd have touches for the virtual joystick and each individual button.
Note that this approach works well for multitouch elements, too. If you have a map that you want to be zoomable, you can keep track of two touches so that you can compare them for pinch gestures. That way, if your pinch gesture accidentally strays over a button, you've already marked it as part of the pinch gesture, and know not to start triggering that button.
But again, a better approach would be to have a separate SKSpriteNode subclass that just tracks the joystick touches and reports its state to some higher-level manager class. You already know everything you need to know to do this - it's like what you have without all the extra checking to see if there are other buttons pressed. Same with the buttons. The only new part would be messaging "up the chain" to a manager, and that's pretty straightforward to deal with.

changing the height of UITabBar in iOS7/8?

I am trying to change the height of the stock UITabBar to 44px, similar to Tweetbot's tab bar height. I've also seen a few other apps do this as well.
however, when i try to set the height it still remains the same
self.tabBar.frame.height = 40
are we not allowed to change the tab bar height? and if so what is a good alternative? using a toolbar?
It seems everybody says this can't be done easily
In your storyboard give your UITabBar a custom subclass name, then implement the subclass with the following
This tells all views that use the tab bar that it should be a certain height.
#implementation MyTabBar
-(CGSize)sizeThatFits:(CGSize)size
{
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = 100;
return sizeThatFits;
}
#end
SomeGuy's answer above worked for me. Here's the Swift translation for anyone who may need it. I made the height close to what it seems most popular apps use.
class TabBar: UITabBar {
override func sizeThatFits(size: CGSize) -> CGSize {
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = 38
return sizeThatFits
}
}
For Swift 3 and xcode 8
extension UITabBar {
override open func sizeThatFits(_ size: CGSize) -> CGSize {
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = 80 // adjust your size here
return sizeThatFits
}
}
In your UITabBarController
- (void)viewWillLayoutSubviews {
CGRect tabFrame = self.tabBar.frame;
tabFrame.size.height = 80;
tabFrame.origin.y = self.view.frame.size.height - 80;
self.tabBar.frame = tabFrame;
}
In swift it is even simpler than all solutions suggested above by using an extensions to UITabBar, no subclassing necessary:
extension UITabBar {
override public func sizeThatFits(size: CGSize) -> CGSize {
super.sizeThatFits(size)
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = <Insert your height here>
return sizeThatFits
}
}
If you have auto layout enabled, you will need to override instrinsicContentSize instead
Proper usage of intrinsicContentSize and sizeThatFits: on UIView Subclass with autolayout
class TabBar: UITabBar {
override func intrinsicContentSize() -> CGSize {
var intrinsicSize = super.frame.size
intrinsicSize.height = 120
return intrinsicSize
}
}
The developer doesn't own the tabBar, the framework does. It will fight you to make sure that the tabBar stays the same height. If you want to work around this, you can make your own toolbar and add autlayout constraints to its height to force it to stay whatever height you'd like.
If you are on iOS 11 then following will help
-(CGSize)sizeThatFits:(CGSize)size
{
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = 60;
if (#available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
sizeThatFits.height += bottomPadding;
}
return sizeThatFits;
}
Basically need to cover safe area, else tabbar height on iPhone X appears to be low.
For iOS11 and above you can use below:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (#available(iOS 11.0, *)) {
self.additionalSafeAreaInsets = UIEdgeInsetsMake(0, 0, 20, 0);
}
}
And for all os, create UITabBar subclass, use it in the UITabBarController and implement below method in the implementation of custom tab bar class:
-(CGSize)sizeThatFits:(CGSize)size {
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = kBarHeight;
return sizeThatFits;
}

Disable rotation for one UITabbar item

I have a uitabbarcontroller with 4 tab bar items and each tab bar item has a uinavigationcontroller.
I needed to lock the orientation of one uitabbar item only to Portrait. So i implemented the following code:
Created a custom tab bar controller and added the following code in it:
MainTabBarController.m
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// You do not need this method if you are not supporting earlier iOS Versions
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
-(NSUInteger)supportedInterfaceOrientations
{
if (self.selectedViewController)
return [self.selectedViewController supportedInterfaceOrientations];
return UIInterfaceOrientationMaskPortrait;
}
-(BOOL)shouldAutorotate
{
return YES;
}
Created a custom navigation controller to use in one of the uitabbaritems and added the following code in it:
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
-(BOOL)shouldAutorotate
{
return YES;
}
and for the uiviewcontroller in the custom navigation controller i added the following code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
The above code works fine. My issue is, if you go to the tabbar item (whose orientation is locked to Protrait) when the device is already in Landscape mode the orientation changes to Landscape. Can anyone please help me how to solve my issue.
Thanks,
Anand.
FYI!!!
I've found a way for my problem. I added the following function to the viewcontroller for which i want the display only in protrait mode:
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
{
if ([[UIDevice currentDevice] respondsToSelector:#selector(setOrientation:)])
{
int orientationPortrait = UIInterfaceOrientationPortrait;
NSMethodSignature *sig = [[UIDevice currentDevice] methodSignatureForSelector:#selector(setOrientation:)];
NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig];
[invo setTarget:[UIDevice currentDevice]];
[invo setSelector:#selector(setOrientation:)];
[invo setArgument:&orientationPortrait atIndex:2];
[invo invoke];
}
}
}