I beginner learning Swift 3 - UISwipe gesture but cannot work if using Array.
This my code.
How can I code so the Label only change from "hello1" to "hello2" then swipe left again to "hello3". Also reverse swipe to right back from "hello3" to "hello2" and "hello1". or loop back to first one.
Thanks.
class ChangeLabelViewController: UIViewController {
var helloArray = ["Hello1", "Hello2", "Hello3"]
var currentArrayIndex = 0
#IBOutlet weak var helloLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ChangeLabelViewController.handleSwipes(sender:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ChangeLabelViewController.handleSwipes(sender:)))
leftSwipe.direction = .left
rightSwipe.direction = .right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
helloLabel.text = helloArray[currentArrayIndex]
}
func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
helloLabel.text = helloArray[currentArrayIndex + 1]
}
if sender.direction == .right {
}
}
Try this:
if sender.direction == .left {
currentArrayIndex = (currentArrayIndex + 1) % 3
helloLabel.text = helloArray[currentArrayIndex]
}
if sender.direction == .right {
currentArrayIndex = (currentArrayIndex + 3 - 1) % 3 //if uInt
helloLabel.text = helloArray[currentArrayIndex]
}
you can also do it with below code,
func handleSwipes(sender: UISwipeGestureRecognizer) {
if sender.direction == .left {
if(currentArrayIndex < helloArray.count - 1)
{
currentArrayIndex += 1
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
}
if sender.direction == .right {
if(currentArrayIndex > 0)
{
currentArrayIndex -= 1
if currentArrayIndex == -1
{
currentArrayIndex = 0
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
else {
let indexPath = IndexPath(item: currentArrayIndex, section: 0)
helloLabel.text = helloArray[indexPath.row]
}
}
}
Related
I'm trying to present a UI of a title/less window when a mouse leaves a window's title or contentview, but not when moving from one to the other; in essence have the two tracking areas function as one (I resorted to this as I couldn't figure out how to create a single area when the title view is hidden):
override func mouseEntered(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
if theEvent.modifierFlags.contains(.shift) {
NSApp.activate(ignoringOtherApps: true)
}
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close entered")
closeButton?.image = closeButtonImage
break
default:
Swift.print(String(format: "%# entered",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = true
updateTranslucency()
// view or title entered
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: !lastMouseOver)
}
}
}
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
let lastMouseOver = mouseOver
mouseOver = false
updateTranslucency()
if hideTitle && (lastMouseOver != mouseOver) {
updateTitleBar(didChange: lastMouseOver)
}
}
}
Additionally, there's a tracking rect on the close button to appear only when over. Anyway, from my tracer output I see the issue - mouse over window from beneath to over its title:
view entered
updateTitleBar
**view entered**
view exited
updateTitleBar
title entered
updateTitleBar
title exited
Note sure why I'm getting a second view entered event (view entered), but the movement out of the view and onto the adjacent title each triggers an updateTilebar() call which is visually stuttering - not remedied so far with animation:
fileprivate func docIconToggle() {
let docIconButton = panel.standardWindowButton(.documentIconButton)
if settings.autoHideTitle.value == false || mouseOver {
if let doc = self.document {
docIconButton?.image = (doc as! Document).displayImage
}
else
{
docIconButton?.image = NSApp.applicationIconImage
}
docIconButton?.isHidden = false
self.synchronizeWindowTitleWithDocumentName()
}
else
{
docIconButton?.isHidden = true
}
}
#objc func updateTitleBar(didChange: Bool) {
if didChange {
Swift.print("updateTitleBar")
if settings.autoHideTitle.value == true && !mouseOver {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.hidden
panel.animator().titlebarAppearsTransparent = true
panel.animator().styleMask.formUnion(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
} else {
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 1.0
panel.animator().titleVisibility = NSWindowTitleVisibility.visible
panel.animator().titlebarAppearsTransparent = false
panel.animator().styleMask.formSymmetricDifference(.fullSizeContentView)
}, completionHandler: {
self.docIconToggle()
})
}
}
}
So my question is about how to defer the actions when areas are adjacent.
They (titlebar & content view) are not siblings of each other so didn't think a hitTest() was doable but basically if I could tell if I was moving into the adjacent tracking area, I'd like it to be a no-op.
Any help with why animation isn't working would be a plus.
Not a true answer, but if you know the adjacent view's rect you can use the event's location to probe whether you'd want to ignore movements among adjacent views:
override func mouseExited(with theEvent: NSEvent) {
let hideTitle = (doc?.settings.autoHideTitle.value == true)
let location : NSPoint = theEvent.locationInWindow
switch theEvent.trackingNumber {
case closeTrackingTag:
Swift.print("close exited")
closeButton?.image = nullImage
break
default:
let vSize = self.window?.contentView?.bounds.size
// If we exit to the title bar area we're still "inside"
// and visa-versa, leaving title to content view.
if theEvent.trackingNumber == titleTrackingTag, let tSize = titleView?.bounds.size {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y < ((vSize?.height)! + tSize.height) {
Swift.print("title -> view")
return
}
}
else
if theEvent.trackingNumber == viewTrackingTag {
if location.x >= 0.0 && location.x <= (vSize?.width)! && location.y > (vSize?.height)! {
Swift.print("view -> title")
return
}
}
mouseOver = false
updateTranslucency()
if hideTitle {
updateTitleBar(didChange: true)
}
Swift.print(String(format: "%# exited",
(theEvent.trackingNumber == titleTrackingTag
? "title" : "view")))
}
}
Hello i am trying to make a shop page on my application. The shop page contains a table with a title that is clickable so it expands into subcategories of that title. Etc click coffee and get some different kind of coffee to buy from.
Title = Drinks, Sandwiches & Drinks in the picture below.
subcategory from title = Ham & cheese, Chicken, bacon & curry .... and so on.
Now the expanding and closing the titles work fine. But when i press + (add iteem to cart) i get a out out of index if it's clicked in a specific order.
if i expand sandwiches (like it is in the picture) and then expand drinks (the one below sandwiches) and then click on the plus on any subcategory in sandwiches and then close sandwiches again and press on plus on any subcategory in Drinks (the one below the now closed sandwiches) the application will crash with a out of index error. Only this combination gives this error.
How the code works:
/* Create Cells */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -
> UITableViewCell {
// Row is DefaultCell
if let rowData = destinationData?[indexPath.row] {
let defaultCell = Bundle.main.loadNibNamed("TableViewCellMenuItems", owner: self, options: nil)?.first as! TableViewCellMenuItems
// tableView.dequeueReusableCell(withIdentifier: "TableViewCellMenuItems", for: indexPath).first as! TableViewCellMenuItems
defaultCell.menuName.text = rowData.name
defaultCell.menuPicture.image = rowData.image
defaultCell.menuFoldPic.image = #imageLiteral(resourceName: "ic_keyboard_arrow_down")
defaultCell.selectionStyle = .none
return defaultCell
}
// Row is ExpansionCell
else {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: indexPath.row)] {
// print("her åbner vi")
// // Create an ExpansionCell
let expansionCell =
Bundle.main.loadNibNamed("TableViewCellMenuItemExpanded", owner: self, options: nil)?.first as! TableViewCellMenuItemExpanded
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: indexPath.row)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = indexPath.row - parentCellIndex - 1
// Set the cell's data
expansionCell.itemPrice.text = String (describing: "\(rowData.menuItems![flightIndex].price) kr")
expansionCell.itemName.text = rowData.menuItems?[flightIndex].name
expansionCell.itemCount.text = String (describing: rowData.menuItems![flightIndex].count)
// expansionCell.itemCount.text = String (describing: indexPath.row)
expansionCell.itemAdd.tag = Int(indexPath.row)
expansionCell.itemMinus.tag = Int(indexPath.row)
expansionCell.itemAdd.addTarget(self, action: #selector(HomeSelectedShopViewController.plus(_:)), for: .touchUpInside)
expansionCell.itemMinus.addTarget(self, action: #selector(HomeSelectedShopViewController.minus(_:)), for: .touchUpInside)
expansionCell.selectionStyle = .none
return expansionCell
}
}
return UITableViewCell()
}
as you can see the expansionCells get a tag for my function plus and minus (so i can know which indexPath i should add up or down)
Plus function:
func plus(_ sender: AnyObject?) {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: sender!.tag)] {
self.table.reloadData()
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: sender!.tag)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = sender!.tag - parentCellIndex - 1
print(sender!.tag)
print(flightIndex)
print(rowData.menuItems!.count)
var con = 0;
for a in rowData.menuItems! {
print("her er navn : \(a.name) og her er id \(con)")
con += 1
}
let data = rowData.menuItems![flightIndex]
let item = Items(name: data.name, price: data.price, count: data.count)
var counter = 0;
for x in self.basket {
if(x.name == item.name || x.price == item.price)
{
counter = counter+1;
}
}
if(counter >= 99)
{
}
else
{
DispatchQueue.main.async {
self.basket.append(item)
self.updateBasket()
rowData.menuItems![flightIndex].count = rowData.menuItems![flightIndex].count+1;
self.table.reloadData()
}
}
}
}
Rest of the functions:
/* Expand cell at given index */
private func expandCell(tableView: UITableView, index: Int) {
// print("when is this run 1")
// Expand Cell (add ExpansionCells
if let flights = destinationData?[index]?.menuItems {
for i in 1...flights.count {
destinationData?.insert(nil, at: index + i)
tableView.insertRows(at: [NSIndexPath(row: index + i, section: 0) as IndexPath] , with: .top)
}
}
}
/* Contract cell at given index */
private func contractCell(tableView: UITableView, index: Int) {
// print("when is this run 4")
if let flights = destinationData?[index]?.menuItems {
for i in 1...flights.count {
destinationData?.remove(at: index+1)
tableView.deleteRows(at: [NSIndexPath(row: index+1, section: 0) as IndexPath], with: .top)
}
}
}
/* Get parent cell index for selected ExpansionCell */
private func getParentCellIndex(expansionIndex: Int) -> Int {
// print("when is this run 5")
var selectedCell: Menu?
var selectedCellIndex = expansionIndex
while(selectedCell == nil && selectedCellIndex >= 0) {
selectedCellIndex -= 1
selectedCell = destinationData?[selectedCellIndex]
}
return selectedCellIndex
}
func minus(_ sender: AnyObject?) {
if let rowData = destinationData?[getParentCellIndex(expansionIndex: sender!.tag)] {
// Get the index of the parent Cell (containing the data)
let parentCellIndex = getParentCellIndex(expansionIndex: sender!.tag)
// Get the index of the flight data (e.g. if there are multiple ExpansionCells
let flightIndex = sender!.tag - parentCellIndex - 1
let data = rowData.menuItems![flightIndex]
let item = Items(name: data.name, price: data.price, count: data.count)
DispatchQueue.main.async {
var counter = 0;
for x in self.basket {
if(x.name == item.name || x.price == item.price)
{
if(item.count == 0)
{
break
}
else
{
self.basket.remove(at: counter)
self.updateBasket()
rowData.menuItems![flightIndex].count = rowData.menuItems![flightIndex].count-1;
self.table.reloadData()
break
}
}
counter = counter+1;
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let data = destinationData?[indexPath.row] {
// If user clicked last cell, do not try to access cell+1 (out of range)
if(indexPath.row + 1 >= (destinationData?.count)!) {
expandCell(tableView: tableView, index: indexPath.row)
}
else {
// If next cell is not nil, then cell is not expanded
if(destinationData?[indexPath.row+1] != nil) {
expandCell(tableView: tableView, index: indexPath.row)
} else {
contractCell(tableView: tableView, index: indexPath.row)
}
}
}
}
I dont really know how to solve this, all i know is that the plus function gets out of range on this bit "let data = rowData.menuItems![flightIndex]".
I am trying to save a simple piece of information using NSUserdefaults. I am trying to save a SKSprite to have an alpha of 1. Here is how I am doing it.
First scene: Level select (sprite alpha is 0.2)
When user completes Level: (edit sprite in Level Select to equal one)
GameViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = levelselectscene {
// Set the scale mode to scale to fit the window
scene.scaleMode = .fill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
Level Select:
override func didMove(to view: SKView) {
if unlockLevelTwoButton == true {
levelselectscene?.childNode(withName: "LevelTwoButton")?.alpha = 1
UserDefaults.standard.set(unlockLevelTwoButton, forKey: "LevelTwoUnlocked")
print("I got this far")
}
}
Level One:
func didBegin(_ contact: SKPhysicsContact) {
var bodyA = contact.bodyA
var bodyB = contact.bodyB
let threeStars = SKScene(fileNamed: "LevelCompleted3Star")
let fadeAction = SKAction.fadeAlpha(by: 1, duration: 0.45)
if bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 2 || bodyA.categoryBitMask == 2 && bodyB.categoryBitMask == 1{
print("TEST")
levelOneCompleted() //islevelonecompleted
unlockLevelTwoButton = true
//3 stars
threeStars?.scaleMode = .fill
self.view?.presentScene(threeStars!, transition: .fade(withDuration: 0.3))
}
3 Stars:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isLevelOneCompleted == true{
unlockLevelTwoButton = true
UserDefaults.standard.set(isLevelOneCompleted, forKey: "LevelOne")
UserDefaults.standard.synchronize()
levelselectscene?.scaleMode = .fill
levelselectscene?.childNode(withName: "levelTwoButton")?.alpha = 1
self.view?.presentScene(levelselectscene)
}
To me, it looks like the information should save. What am I doing wrong? I also have the keys set to retrieve:
if let z = UserDefaults.standard.object(forKey: "LevelTwoButton")
{
unlockLevelTwoButton = z as! Bool
}
Can't figure out why it's not saving!
Based on the code you've shown, you are saving it with one name, and retrieving it with a different name (LevelTwoUnlocked) vs (LevelTwoButton)
I am trying to make a numbered list out of the information the user inputs into a UITextView. For example,
List item one
List item two
List item three
Here is the code that I have tried but does not give me the desired effect.
var currentLine: Int = 1
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Add "1" when the user starts typing into the text field
if (textView.text.isEmpty && !text.isEmpty) {
textView.text = "\(currentLine). "
currentLine += 1
}
else {
if text.isEmpty {
if textView.text.characters.count >= 4 {
let str = textView.text.substring(from:textView.text.index(textView.text.endIndex, offsetBy: -4))
if str.hasPrefix("\n") {
textView.text = String(textView.text.characters.dropLast(3))
currentLine -= 1
}
}
else if text.isEmpty && textView.text.characters.count == 3 {
textView.text = String(textView.text.characters.dropLast(3))
currentLine = 1
}
}
else {
let str = textView.text.substring(from:textView.text.index(textView.text.endIndex, offsetBy: -1))
if str == "\n" {
textView.text = "\(textView.text!)\(currentLine). "
currentLine += 1
}
}
}
return true
}
as suggested here: How to make auto numbering on UITextview when press return key in swift but had no success.
Any help is much appreciated.
You can subclass UITextView, override method willMove(toSuperview and add an observer for UITextViewTextDidChange with a selector that break up your text into lines enumerating and numbering it accordingly. Try like this:
class NumberedTextView: UITextView {
override func willMove(toSuperview newSuperview: UIView?) {
frame = newSuperview?.frame.insetBy(dx: 50, dy: 80) ?? frame
backgroundColor = .lightGray
NotificationCenter.default.addObserver(self, selector: #selector(textViewDidChange), name: .UITextViewTextDidChange, object: nil)
}
func textViewDidChange(notification: Notification) {
var lines: [String] = []
for (index, line) in text.components(separatedBy: .newlines).enumerated() {
if !line.hasPrefix("\(index.advanced(by: 1))") &&
!line.trimmingCharacters(in: .whitespaces).isEmpty {
lines.append("\(index.advanced(by: 1)). " + line)
} else {
lines.append(line)
}
}
text = lines.joined(separator: "\n")
// this prevents two empty lines at the bottom
if text.hasSuffix("\n\n") {
text = String(text.characters.dropLast())
}
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let textView = NumberedTextView()
view.addSubview(textView)
}
}
how do we create a UICollectionViewLayout like the SnapChat's stories?
Any ideas please?
I'd like to have a solution without external library.
Based on a precedent answer adapted to your issue:
-(id)initWithSize:(CGSize)size
{
self = [super init];
if (self)
{
_unitSize = CGSizeMake(size.width/2,80);
_cellLayouts = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void)prepareLayout
{
for (NSInteger aSection = 0; aSection < [[self collectionView] numberOfSections]; aSection++)
{
//Create Cells Frames
for (NSInteger aRow = 0; aRow < [[self collectionView] numberOfItemsInSection:aSection]; aRow++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:aRow inSection:aSection];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSUInteger i = aRow%3;
NSUInteger j = aRow/3;
CGFloat offsetY = _unitSize.height*2*j;
CGPoint xPoint;
CGFloat height = 0;
BOOL invert = NO;
if (aRow%6 >= 3) //We need to invert Big cell and small cells => xPoint.x
{
invert = YES;
}
switch (i)
{
case 0:
xPoint = CGPointMake((invert?_unitSize.width:0), offsetY);
height = _unitSize.height;
break;
case 1:
xPoint = CGPointMake((invert?_unitSize.width:0), offsetY+_unitSize.height);
height = _unitSize.height;
break;
case 2:
xPoint = CGPointMake((invert?0:_unitSize.width), offsetY);
height = _unitSize.height*2;
break;
default:
break;
}
CGRect frame = CGRectMake(xPoint.x, xPoint.y, _unitSize.width, height);
[attributes setFrame:frame];
[_cellLayouts setObject:attributes forKey:indexPath];
}
}
}
I set the height of unitSize to 80, but you can use the size of the screen if needed, like _unitSize = CGSizeMake(size.width/2,size.height/4.);.
That render:
Side note: It's up to you to adapt the logic, or do changes, the cell frames calculation may not be the "best looking piece of code".
UICollectionViewLayout like the SnapChat's stories Like
Swift 3.2 Code
import Foundation
import UIKit
class StoryTwoColumnsLayout : UICollectionViewLayout {
fileprivate var cache = [IndexPath: UICollectionViewLayoutAttributes]()
fileprivate var cellPadding: CGFloat = 4
fileprivate var contentHeight: CGFloat = 0
var oldBound: CGRect!
let numberOfColumns:Int = 2
var cellHeight:CGFloat = 255
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
super.prepare()
contentHeight = 0
cache.removeAll(keepingCapacity: true)
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
if collectionView.numberOfSections == 0 {
return
}
let cellWidth = contentWidth / CGFloat(numberOfColumns)
cellHeight = cellWidth / 720 * 1220
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * cellWidth)
}
var column = 0
var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
var newheight = cellHeight
if column == 0 {
newheight = ((yOffset[column + 1] - yOffset[column]) > cellHeight * 0.3) ? cellHeight : (cellHeight * 0.90)
}
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: cellWidth, height: newheight)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache[indexPath] = (attributes)
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + newheight
if column >= (numberOfColumns - 1) {
column = 0
} else {
column = column + 1
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
visibleLayoutAttributes = cache.values.filter({ (attributes) -> Bool in
return attributes.frame.intersects(rect)
})
print(visibleLayoutAttributes)
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
// print(cache[indexPath.item])
return cache[indexPath]
}
}