I'm creating a game where the player has the possibility to add several players on a view controller :
Actually, I can add a new player and remove it :
The problem appears when I remove a player :
I want player 2 and player 3 goes below the textField "Name when I remove a player. How can I do this ?
ViewDidLoad :
// MARK: ADD BUTTON
let addButtonSize = CGSize(width: topPartSize.height, height: topPartSize.height)
let addButton = UIButton(frame: CGRect(x: enterPlayerPartSize.width - addButtonSize.width, y: 0, width: addButtonSize.width, height: addButtonSize.height))
addButton.titleLabel!.font = UIFont.fontAwesomeOfSize(addButtonSize.height / 2)
addButton.setTitle(String.fontAwesomeIconWithName(.Plus), forState: .Normal)
addButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
addButton.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 0.5), forState: .Highlighted)
addButton.titleLabel?.textAlignment = .Center
addButton.layer.zPosition = 3
addButton.addTarget(self, action: #selector(PlayersViewController.addPlayer(_:)), forControlEvents: .TouchUpInside)
enterPlayerPart.addSubview(addButton)
// MARK: TEXT FIELD
let textFieldSize = CGSize(width: enterPlayerPartSize.width - playerIconSize.width - addButtonSize.width, height: enterPlayerPartSize.height)
textField = UITextField(frame: CGRect(x: playerIconSize.width, y: 0, width: textFieldSize.width, height: textFieldSize.height))
textField.font = UIFont(name: "Avenir-Light", size: textFieldSize.height / 2)
textField.attributedPlaceholder = NSAttributedString(string:"Name", attributes:[NSForegroundColorAttributeName: UIColor(red: 1, green: 1, blue: 1, alpha: 0.5)])
textField.textColor = UIColor.whiteColor()
textField.textAlignment = NSTextAlignment.Left
textField.returnKeyType = UIReturnKeyType.Done
textField.delegate = self
enterPlayerPart.addSubview(textField)
Functions :
// MARK: ADD PLAYER - FUNCTION
func addPlayer(sender: UIButton) {
if textField.text == nil || textField.text == "" {
print("Enter player name")
} else {
createNewPlayer(textField.text!)
}
}
// MARK: CREATE NEW PLAYER - FUNCTION
func createNewPlayer(playerName: String) {
let playerPartSize = CGSize(width: screenWidth, height: screenHeight * 0.1)
let playerPart = UIView(frame: CGRect(x: 0, y: playerPartSize.height * CGFloat(playersCount), width: playerPartSize.width, height: playerPartSize.height))
scrollView.addSubview(playerPart)
// PLAYER ICON
let playerIconSize = CGSize(width: playerPartSize.height, height: playerPartSize.height)
let playerIcon = UILabel(frame: CGRect(x: 0, y: 0, width: playerIconSize.width, height: playerIconSize.height))
playerIcon.font = UIFont.fontAwesomeOfSize(topPartSize.height / 2)
playerIcon.text = String.fontAwesomeIconWithName(.User)
playerIcon.textAlignment = .Center
playerIcon.textColor = UIColor.whiteColor()
playerPart.addSubview(playerIcon)
// REMOVE BUTTON
let removeButtonSize = playerIconSize
let removeButton = UIButton(frame: CGRect(x: playerPartSize.width - removeButtonSize.width, y: 0, width: playerIconSize.width, height: playerIconSize.height))
removeButton.titleLabel!.font = UIFont.fontAwesomeOfSize(removeButtonSize.height / 2)
removeButton.setTitle(String.fontAwesomeIconWithName(.Trash), forState: .Normal)
removeButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
removeButton.setTitleColor(UIColor(red: 1, green: 1, blue: 1, alpha: 0.5), forState: .Highlighted)
removeButton.titleLabel?.textAlignment = .Center
removeButton.addTarget(self, action: #selector(PlayersViewController.removePlayer(_:)), forControlEvents: .TouchUpInside)
playerPart.addSubview(removeButton)
// PLAYER NAME
let playerNameSize = CGSize(width: playerPartSize.width - playerIconSize.width - removeButtonSize.width, height: playerPartSize.height)
let playerNameLabel = UILabel(frame: CGRect(x: playerIconSize.width, y: 0, width: playerNameSize.width, height: playerNameSize.height))
playerNameLabel.font = UIFont(name: "Avenir-Light", size: playerNameSize.height / 2)
playerNameLabel.text = playerName
playerNameLabel.textAlignment = .Left
playerNameLabel.textColor = UIColor.whiteColor()
playerPart.addSubview(playerNameLabel)
// BOTTOM LINE
let bottomLineHeight = playerPartSize.height / 50
let bottomLine = UIView(frame: CGRect(x: 0, y: playerPartSize.height - bottomLineHeight, width: playerPartSize.width, height: bottomLineHeight))
bottomLine.backgroundColor = UIColor(hexColor: 0x1B1B1B)
playerPart.addSubview(bottomLine)
// PLAYERS COUNT ++
playersCount += 1
}
Thanks for your help !
You have two ways to perform it :
You seem using reusable components, so you should have a look on UITableView / UICollectionView. It will manage memory usage, and components layout
If you dont want to migrate to theses components, it recommend you to store all your player views in an array, and relayout them after an add/remove operation
something like :
var players : [UIView] = []
func createNewPlayer(playerName: String) {
//Do your stuff here
players.append(newCreatedPalyerView)
layout()
}
func layout() {
var yPosition = 0
for view in players {
view.frame.origin.y = yPosition
yPosition += view.frame.size.height
}
}
func removePlayer(player : UIView) {
self.players.remove(player)
self.layout()
}
You need to manage your scenario something like below :
First take all the UI of one player in one UIView. So you should have all your one player's stuff(playername label, remove button etc) in one view. (i think you have it in playerPart - that`s cool)
Now you are adding that playerPart directly in scrollview i think, If it is like that then you should not do like this. You should have one UIView of same size of scrollview in scrollview(same as content size of scrollview) and you should add playerPart to that view.
Now you can get all subviews of that view in array something like :
NSArray *viewArr = [tempView subviews];
Assume tempView is vied over scrollview as i have mentioned above,
you can get subview of scrollview also by calling same method.
Now for example you have four player(four view) in view (temp view or scrollview if tempview is not added on it) and you click remove on player 3 then you can get index of that object from that view array. for example,
you have array of total view which you have get from
NSArray *viewArr = [tempView subviews];
then you can get index of player3 i.e view3 like,
int index = [viewArr indexOfObject:player3];
now make one method or function which is called after every removed call and pass that index as parameter in that method.
In that method take one for loop, which start from this index and run till last object (view) of this viewArr. Decrease y of every coming view in this for loop so your all view which was belowed removed view will goes up.
Here is one my method i was using in my one project to remove view and manage other accordingly :
#pragma mark UpdateFrameOfViews
-(void)updateFrameOfViewWithTag : (int) myTag increament : (CGFloat)increament fromIndex : (int)index{
CustomSubClassView *tempView = [self.view viewWithTag:myTag];
UIScrollView *tempScroll = (UIScrollView*)[tempView superview];
NSArray *subViews = [tempView subviews];
BOOL status = NO;
for (int i = index; i <= subViews.count; i++) {
if (i < subViews.count) {
UIView *tempView1 = [subViews objectAtIndex:i];
CGFloat newY = tempView1.frame.origin.y + increament;
CGRect newFrame = CGRectMake(tempView1.frame.origin.x, newY, tempView1.frame.size.width, tempView1.frame.size.height);
tempView1.frame = newFrame;
status = YES;
}
if (index == subViews.count && i == subViews.count) {
// UIView *tempView1 = [subViews lastObject];
//
// CGFloat newY = tempView1.frame.origin.y + increament;
//
// CGRect newFrame = CGRectMake(tempView1.frame.origin.x, newY, tempView1.frame.size.width, tempView1.frame.size.height);
//
// tempView1.frame = newFrame;
status = YES;
}
// tempView1.backgroundColor = [UIColor orangeColor];
}
if (status) {
CGRect containerFrame = CGRectMake(0, 0, self.view.frame.size.width, tempView.frame.size.height+increament);
tempView.frame = containerFrame;
tempScroll.contentSize = CGSizeMake(tempView.frame.size.width, tempView.frame.size.height);
}
}
This method is for reference just, because here scenario is little bit different.
My code is in objective and is for reference only convert in swift required!!
This was how you manage it!!
Suggestion : it is better to use UITableView in your case!!!
Hope this will help :)
Related
I have set up the activity indicator while loading to next view. The original view contains a tableView of buttons and it will direct to the next view when the button is clicked. However, it doesn't show up when coming back to the original view. It works on another view transition but I don't know why it doesn't work here.
Here's the button clicking function of processing to next view:
#objc func remark(_ sender: UIButton) {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
DispatchQueue.main.async {
self.markDetailsDelegate?.getStudentList(paperId: sender.paperId!, schoolClass: sender.schoolClass!, studentToShow: sender.studentToShow, questionNo: sender.questionNo!, readOnlyFlag: false) { returnList, studentReturnList, returnMarkBox, returnMCOptionBox, returnStudentMarkList, error in
self.pdfFileList = returnList!
self.studentList = studentReturnList!
self.questionMarkBox = returnMarkBox!
self.mcQuestionOptionBox = returnMCOptionBox!
self.studentMarkList = returnStudentMarkList!
}
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
}
}
}
Function of reloading the tableView (once it's returning back to the tableView, it is called to reload it):
#objc func reloadMarkDetails() {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
DispatchQueue.main.async {
self.markDetailsDelegate?.reloadMarkDetails(paperId: self.currentPaperId, schoolClass: self.currentSchoolClass) { returnPDFFileList, returnStudentList, returnQuestionMarkBox, returnMCQuestionOptionBox, returnStudentMarkList, returnPaperCommentList, returnPaperSummary, returnMarkSummary, returnTagSummary, returnLastMarkedSummary, returnAnsNoOfPage, returnDatetime, returnPaperFreezedFlag, error in
self.studentList = returnStudentList
self.studentMarkList = returnStudentMarkList
self.markSummary = returnMarkSummary
self.lastMarkedSummary = returnLastMarkedSummary
self.paperFreezedFlag = returnPaperFreezedFlag
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
}
}
}
Utility class of managing activity indicator:
class ViewControllerUtils: UIView {
static var shared = ViewControllerUtils()
var container: UIView = UIView()
var loadingView: UIView = UIView()
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
var loadingTextLabel = UILabel()
let margin: CGFloat = 20.0
/*
Show customized activity indicator (with text),
actually add activity indicator to passing view
#param uiView - add activity indicator to this view
#param title - add title to activity indicator
*/
func showActivityIndicator(uiView: UIView, title: String) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColorFromHex(rgbValue: 0x444444, alpha: 0.2)
loadingView.frame = CGRect(x: 0, y: 0, width: 230, height: 60)
loadingView.center = uiView.center
loadingView.backgroundColor = UIColorFromHex(rgbValue: 0xffffff, alpha: 1.0)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingView.layer.shadowColor = UIColor.black.cgColor
loadingView.layer.shadowOpacity = 1
loadingView.layer.shadowOffset = .zero
loadingView.layer.shadowRadius = 10
activityIndicator.frame = CGRect(x: 0.0, y: 5.0, width: 40, height: 40);
activityIndicator.style = UIActivityIndicatorView.Style.gray
activityIndicator.center = CGPoint(x: (activityIndicator.frame.size.width + margin) / 2, y: loadingView.frame.size.height / 2)
loadingTextLabel.textColor = UIColor.black
loadingTextLabel.text = title
loadingTextLabel.font = .systemFont(ofSize: 16, weight: .medium)
loadingTextLabel.textAlignment = .center
loadingTextLabel.sizeToFit()
loadingTextLabel.adjustsFontSizeToFitWidth = true
loadingTextLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
loadingTextLabel.frame = CGRect(x: activityIndicator.frame.size.width + margin * 0.5, y: activityIndicator.frame.origin.y, width: loadingView.frame.size.width - activityIndicator.frame.size.width - margin, height: activityIndicator.frame.size.height)
loadingView.addSubview(activityIndicator)
loadingView.addSubview(loadingTextLabel)
container.addSubview(loadingView)
uiView.superview?.addSubview(container)
activityIndicator.startAnimating()
}
/*
Show customized activity indicator (without text),
actually add activity indicator to passing view
#param uiView - add activity indicator to this view
*/
func showActivityIndicator(uiView: UIView) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColorFromHex(rgbValue: 0xffffff, alpha: 0.5)
loadingView.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
loadingView.center = container.center
loadingView.backgroundColor = UIColorFromHex(rgbValue: 0x444444, alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingView.layer.cornerRadius = 10
loadingView.layer.shadowColor = UIColor.black.cgColor
loadingView.layer.shadowOpacity = 1
loadingView.layer.shadowOffset = .zero
loadingView.layer.shadowRadius = 10
loadingTextLabel.removeFromSuperview()
activityIndicator.frame = CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0);
activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
activityIndicator.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2);
loadingView.addSubview(activityIndicator)
container.addSubview(loadingView)
uiView.superview?.addSubview(container)
activityIndicator.startAnimating()
}
/*
Change text of activity indicator
#param message - modified text
*/
func changeTextToActivityIndicator(message: String) {
loadingTextLabel.text = message
}
/*
Hide activity indicator
Actually remove activity indicator from its super view
#param uiView - remove activity indicator from this view
*/
func hideActivityIndicator(uiView: UIView) {
activityIndicator.stopAnimating()
container.removeFromSuperview()
print("[DEBUG] Stop Activity Indicator at View - \(uiView)")
}
/*
Define UIColor from hex value
#param rgbValue - hex color value
#param alpha - transparency level
*/
func UIColorFromHex(rgbValue:UInt32, alpha:Double=1.0)->UIColor {
let red = CGFloat((rgbValue & 0xFF0000) >> 16)/256.0
let green = CGFloat((rgbValue & 0xFF00) >> 8)/256.0
let blue = CGFloat(rgbValue & 0xFF)/256.0
return UIColor(red:red, green:green, blue:blue, alpha:CGFloat(alpha))
}
}
I tried to figure out why the indicator is not showing while returning back to original view by using Debug View Hierarchy and here are the results:
View A processing to next View B (no problem)
View B returning back to original View A (activity indicator goes to wrong view hierarchy)
Is there anything I did wrong during segue transition? Thanks so much if someone could help me!
I assume it should be
#objc func remark(_ sender: UIButton) {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
self.markDetailsDelegate?.getStudentList(paperId: sender.paperId!, schoolClass: sender.schoolClass!, studentToShow: sender.studentToShow, questionNo: sender.questionNo!, readOnlyFlag: false) { returnList, studentReturnList, returnMarkBox, returnMCOptionBox, returnStudentMarkList, error in
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
self.pdfFileList = returnList!
self.studentList = studentReturnList!
self.questionMarkBox = returnMarkBox!
self.mcQuestionOptionBox = returnMCOptionBox!
self.studentMarkList = returnStudentMarkList!
}
}
}
I'm trying to add a function that will show the close button in my custom alert view.
When I call it for the first time, only an empty area appears that does not contain a button (no color, no text and no pressing). When you call again - everything works as it should.
It does not depend on the parameter animated, in both cases it works the same.
My dialogView is a my custom UIView. I don't use UIAlertController
What could be the problem?
initial view after the first attempt second attempt
GIF call func by click
func showCloseButton(text: String, animated: Bool){
let dialogViewHeight = self.dialogView.frame.height + 50 + 1
let button = UIButton(type: .roundedRect)
button.backgroundColor = .red
button.frame.origin = CGPoint(x: 0, y: dialogViewHeight)
button.frame.size = CGSize(width: self.dialogView.frame.width, height: 50)
button.setTitle(text, for: .normal)
button.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
let separatorLineView = UIView()
separatorLineView.frame.origin = CGPoint(x: 0, y: self.dialogView.frame.height)
separatorLineView.frame.size = CGSize(width: dialogView.frame.width, height: 1)
separatorLineView.backgroundColor = UIColor.groupTableViewBackground
dialogView.addSubview(separatorLineView)
if animated {
animateAdjustDialogView(height: dialogViewHeight){
Logger.Log("completion did")
self.dialogView.addSubview(button)
}
} else {
Logger.Log("button.frame.origin = \(button.frame.origin)")
Logger.Log("button.frame.size = \(button.frame.size)")
Logger.Log("button.title = \(button.titleLabel)")
self.dialogView.frame.size = CGSize(width: self.dialogView.frame.width, height: dialogViewHeight)
self.dialogView.addSubview(button)
}
}
private func animateAdjustDialogView(height: CGFloat, completion: (()->Void)?){
UIView.animate(withDuration: 1.0, animations: {
self.dialogView.frame.size = CGSize(width: self.dialogView.frame.width, height: height)
self.layoutIfNeeded()
}) { (finished) in
Logger.Log("finished = \(finished)")
if finished {
Logger.Log("completion run")
completion?()
}
}
}
Button frame is causing the issue -
button.frame.origin = CGPoint(x: 0, y: dialogViewHeight)
Where
let dialogViewHeight = self.dialogView.frame.height + 50 + 1
That means, button goes beyond dialogView frame.
So replace
button.frame.origin = CGPoint(x: 0, y: dialogViewHeight)
With
button.frame.origin = CGPoint(x: 0, y: dialogViewHeight - 50) // Height of the Button.
Let me know if you are still having any issue.
For my first challenge using UIScrollView I modified this example to make UIScrollView display not just another background colour but another UIView and UILabel on each page. But I could have just as easily chosen to display objects like UITableView, UIButton or UIImage.
Potentially, UIScrollView could be much more than a giant content view where users scroll from one part to the next, e.g., some pages might have a UIButton that takes a user to a specific page, the same way we use books.
Code Improvements
My question has evolved since I first posted it. Initially the labels piled up on page 1 (as shown below) but this has now been corrected. I also included this extension to make the font larger.
Further improvement ?
As the code evolved I became more aware of other issues e.g. iPhone 5 images (below) appear differently on iPhone 7 where the UILabel is centred but not the UIView. So my next challenge is possibly to learn how to combine UIScrollView with Autolayout. I invite anyone to spot other things that might be wrong.
ViewController.swift (corrected)
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
var views = [UIView]()
var lables = [UILabel]()
var colors:[UIColor] = [UIColor.red, UIColor.magenta, UIColor.blue, UIColor.cyan, UIColor.green, UIColor.yellow]
var frame: CGRect = CGRect.zero
var pageControl: UIPageControl = UIPageControl(frame: CGRect(x: 50, y: 500, width: 200, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
initialiseViewsAndLables()
configurePageControl()
scrollView.delegate = self
self.view.addSubview(scrollView)
for index in 0..<colors.count {
frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
frame.size = self.scrollView.frame.size
self.scrollView.isPagingEnabled = true
views[index].frame = frame
views[index].backgroundColor = colors[Int(index)]
views[index].layer.cornerRadius = 20
views[index].layer.masksToBounds = true
lables[index].frame = frame
lables[index].center = CGPoint(x: (view.frame.midX + frame.origin.x), y: view.frame.midY)
lables[index].text = String(index + 1)
lables[index].defaultFont = UIFont(name: "HelveticaNeue", size: CGFloat(200))
lables[index].textAlignment = .center
lables[index].textColor = .black
let subView1 = views[index]
let subView2 = lables[index]
self.scrollView .addSubview(subView1)
self.scrollView .addSubview(subView2)
}
print(views, lables)
self.scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width * CGFloat(colors.count), height: self.scrollView.frame.size.height)
pageControl.addTarget(self, action: Selector(("changePage:")), for: UIControlEvents.valueChanged)
}
func initialiseViewsAndLables() {
// Size of views[] and lables[] is linked to available colors
for index in 0..<colors.count {
views.insert(UIView(), at:index)
lables.insert(UILabel(), at: index)
}
}
func configurePageControl() {
// Total number of available pages is based on available colors
self.pageControl.numberOfPages = colors.count
self.pageControl.currentPage = 0
self.pageControl.backgroundColor = getColour()
self.pageControl.pageIndicatorTintColor = UIColor.black
self.pageControl.currentPageIndicatorTintColor = UIColor.green
self.view.addSubview(pageControl)
}
func getColour() -> UIColor {
let index = colors[pageControl.currentPage]
return (index)
}
func changePage(sender: AnyObject) -> () {
scrollView.setContentOffset(CGPoint(x: CGFloat(pageControl.currentPage) * scrollView.frame.size.width, y: 0), animated: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
pageControl.backgroundColor = getColour()
}
}
Extension
extension UILabel{
var defaultFont: UIFont? {
get { return self.font }
set { self.font = newValue }
}
}
The centre point of the lable on each frame must be offset by the origin of the content view (as Baglan pointed out). I've modified the following line of code accordingly.
lables[Int(index)].center = CGPoint(x: (view.frame.midX + frame.origin.x), y: view.frame.midY)
i really how to do a thing wich is probably easy..
I've a ScrollView with some button in it.
That's how i create all my button in the scrollview.
var buttonList = [UIButton]()
func createButton() {
let imageArray = fillImageArray()
var lastButtonWidth: CGFloat = 0
for index in 0..<6 {
let frame1 = CGRect(x: ((self.view.frame.size.width / 2) - 27.5) + CGFloat(index * 70), y: 0, width: 55, height: 55 )
let button = UIButton(frame: frame1)
button.setImage(imageArray[index], forState: .Normal)
button.tag = index
button.addTarget(parentViewController, action: #selector(ViewController.buttonClicked(_:)), forControlEvents: .TouchUpInside)
self.scrollView.addSubview(button)
lastButtonWidth = frame1.origin.x
buttonList.append(button)
}
self.scrollView.contentSize = CGSizeMake(lastButtonWidth + 55, 0)
}
I want when i press one of my button to center him and positioning correctly the other buttons.
example :
If i press on 5 i want this result :
the button 5 is moved to the center.
Now what I would suggest using is the scroll view method scrollView.scrollRectToVisible(CGRect, animated: Bool). This will move the scroll view to make a certain part of your content visible.
To create the CGRect you could do something like this:
let scrollWidth = scrollView.frame.width
let scrollHeight = scrollView.frame.height
let desiredXCoor = button.frame.origin.x - ((scrollWidth / 2) - (button.frame.width / 2) )
let rect = CGRect(x: desiredXCoor, y: 0, width: scrollWidth, height: scrollHeight)
scrollView.scrollRectToVisible(rect, animated: true)
My math may be a bit off, but the essence is that you use the size of the scrollView and the UIButton to create a CGRect for the scroll view to move to. This means when a button is clicked, you could use an implementation like this:
func myMethod() {
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender: UIButton){
let scrollWidth = scrollView.frame.width
let scrollHeight = scrollView.frame.height
let desiredXCoor = sender.frame.origin.x - ((scrollWidth / 2) - (sender.frame.width / 2) )
let rect = CGRect(x: desiredXCoor, y: 0, width: scrollWidth, height: scrollHeight)
scrollView.scrollRectToVisible(rect, animated: true)
}
If adjusted properly to your project, this should allow you to do what you have outlined.
Hope this would help you.
In button action method write below code.
func buttonClicked(sender:UIButton) {
for view in self.scrollView.subviews {
if view.isKindOfClass(UIButton) && view.tag == sender.tag {
let xCenter = max(0, (view.center.x - self.scrollView.frame.width/2))
self.scrollView.setContentOffset(CGPointMake(xCenter, self.scrollView.contentOffset.y), animated: true)
break;
}
}
}
I suppose you want the button touched to be placed in the center,so there is a easy solution, you can use the button's center.x - scrollview.width / 2 as the offsetX to construct the contentOffset and take two boundary situation into consideration:offsetx < 0 and offsetx + scrollview.width > scroll.contentSize.width
Ok i just found by myself.
let centerScrollView = scrollView.frame.size.height * 2
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.scrollView.contentOffset = CGPoint(x: sender.frame.origin.x - centerScrollView, y: sender.frame.origin.y)
}, completion: nil)
and it's perfectly center. Thx everyone.
I'm looking for examples/tutorials/framework explaining how to do a navigation bar/controller which slide to left and right like Tinder.app and Twitter.app
I'm not talking about the faces swiping thing of Tinder, I'm talking about the top menu and the views we can slide entirely to left or right to go smoothly to other screens of the app like profile, moments, etc
I'm looking around but not find anything really interesting until then, I hope you can point me out something.
I'm afraid that the complete solution to this is quite a bit beyond the scope of a single question.
However in the interest of trying to help you I think it's worth looking into this - That's a link to Cocoa Controls, a website which people build ready to go controls you can just drop into your app. (it's quite a cool site really).
That particular link is to MSSlidingPanelController. Which I think is exactly what you are looking for. The source code is clearly visible so you can see exactly what's required to get the effect you are looking for.
Here are a few other examples. Hope this helps.
MSSlidingPanelController is not what you are looking for. These are "drawer views", which only allows user to swipe to a certain drawer.
TwitterPagingViewer and SwiftPagingNav is exactly like the one on Twitter, only more complicated.
Tinder seems to be using a UIPageViewController with hidden dots, which is done by deleting these methods:
presentationCountForPageViewController
presentationIndexForPageViewController
Here is a good tutorial:
https://www.youtube.com/watch?v=8bltsDG2ENQ
Here is a great repo:
https://github.com/goktugyil/EZSwipeController
If you need it in Swift, I've created this one
(it also works on any screen resolution vs just iPhone 4/5/5s like the other example)
https://github.com/aubrey/SwiftPagingNav
class PageViewController: UIViewController, UIScrollViewDelegate {
var scrollView:UIScrollView!
var pageControl:UIPageControl!
var navbarView:UIView!
var navTitleLabel1:UILabel!
var navTitleLabel2:UILabel!
var navTitleLabel3:UILabel!
var view1:UIView!
var view2:UIView!
var view3:UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.lightGrayColor()
//Creating some shorthand for these values
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
// This houses all of the UIViews / content
scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.clearColor()
scrollView.frame = self.view.frame
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.bounces = false
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.size.width * 3, height: hBounds/2)
//Putting a subview in the navigationbar to hold the titles and page dots
navbarView = UIView()
self.navigationController?.navigationBar.addSubview(navbarView)
//Paging control is added to a subview in the uinavigationcontroller
pageControl = UIPageControl()
pageControl.frame = CGRect(x: 0, y: 35, width: 0, height: 0)
pageControl.backgroundColor = UIColor.whiteColor()
pageControl.numberOfPages = 3
pageControl.currentPage = 0
pageControl.currentPageIndicatorTintColor = UIColor(red:0.325, green:0.667, blue:0.922, alpha: 1)
pageControl.pageIndicatorTintColor = UIColor.whiteColor()
self.navbarView.addSubview(pageControl)
//Titles for the nav controller (also added to a subview in the uinavigationcontroller)
//Setting size for the titles. FYI changing width will break the paging fades/movement
var titleSize = CGRect(x: 0, y: 8, width: wBounds, height: 20)
navTitleLabel1 = UILabel()
navTitleLabel1.frame = titleSize
navTitleLabel1.text = "Home"
navTitleLabel1.textAlignment = NSTextAlignment.Center
self.navbarView.addSubview(navTitleLabel1)
navTitleLabel2 = UILabel()
navTitleLabel2.frame = titleSize
navTitleLabel2.text = "Discover"
navTitleLabel2.textAlignment = NSTextAlignment.Center
self.navbarView.addSubview(navTitleLabel2)
navTitleLabel3 = UILabel()
navTitleLabel3.frame = titleSize
navTitleLabel3.text = "Activity"
navTitleLabel3.textAlignment = NSTextAlignment.Center
self.navbarView.addSubview(navTitleLabel3)
//Views for the scrolling view
//This is where the content of your views goes (or you can subclass these and add them to ScrollView)
view1 = UIView()
view1.backgroundColor = UIColor(red:0.325, green:0.667, blue:0.922, alpha: 1)
view1.frame = CGRectMake(0, 0, wBounds, hBounds)
self.scrollView.addSubview(view1)
self.scrollView.bringSubviewToFront(view1)
//Notice the x position increases per number of views
view2 = UIView()
view2.backgroundColor = UIColor(red:0.231, green:0.529, blue:0.757, alpha: 1)
view2.frame = CGRectMake(wBounds, 0, wBounds, hBounds)
self.scrollView.addSubview(view2)
self.scrollView.bringSubviewToFront(view2)
//Notice the x position increases yet again (wBounds * 2)
view3 = UIView()
view3.backgroundColor = UIColor(red:0.529, green:0.600, blue:0.647, alpha: 1)
view3.frame = CGRectMake(wBounds * 2, 0, wBounds, hBounds)
self.scrollView.addSubview(view3)
self.scrollView.bringSubviewToFront(view3)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
navbarView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 44)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
var xOffset: CGFloat = scrollView.contentOffset.x
//Setup some math to position the elements where we need them when the view is scrolled
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
var widthOffset = wBounds / 100
var offsetPosition = 0 - xOffset/widthOffset
//Apply the positioning values created above to the frame's position based on user's scroll
navTitleLabel1.frame = CGRectMake(offsetPosition, 8, wBounds, 20)
navTitleLabel2.frame = CGRectMake(offsetPosition + 100, 8, wBounds, 20)
navTitleLabel3.frame = CGRectMake(offsetPosition + 200, 8, wBounds, 20)
//Change the alpha values of the titles as they are scrolled
navTitleLabel1.alpha = 1 - xOffset / wBounds
if (xOffset <= wBounds) {
navTitleLabel2.alpha = xOffset / wBounds
} else {
navTitleLabel2.alpha = 1 - (xOffset - wBounds) / wBounds
}
navTitleLabel3.alpha = (xOffset - wBounds) / wBounds
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
var xOffset: CGFloat = scrollView.contentOffset.x
//Change the pageControl dots depending on the page / offset values
if (xOffset < 1.0) {
pageControl.currentPage = 0
} else if (xOffset < self.view.bounds.width + 1) {
pageControl.currentPage = 1
} else {
pageControl.currentPage = 2
}
}
}