Setting up UIContextualAction size - ios

I would like to know if it's possible to set the UIContextualAction size (width and height).
I didn't find any member in UIContextualAction that allow me to do that so I wonder if there is a walk-around to do this?
action.image = UIGraphicsImageRenderer(size:CGSize(width: 35, height: 35 )).image { _ in
UIImage(named:"Delete")?.draw(in: CGRect(x: 0, y: 0, width: 35, height: 35))
}
action.backgroundColor = UIColor.red
return action
Update
I found this post, the idea behind is to use custom button and use UIGesture
https://www.raywenderlich.com/62435/make-swipeable-table-view-cell-actions-without-going-nuts-scroll-views

You might use this syntax to set width and height for UIContextualAction:
let contextAction = UIContextualAction(style: .normal, title: "", handler: {
(param: (Bool) -> Void) in param(true)
})
contextAction.image = UIGraphicsImageRenderer(size: CGSize(160, 90)).image {
_ in UIImage(named: "someObject")!.draw(in: CGRect(40, 10, 200, 100))
}
contextAction.backgroundColor = .green

Related

Why button appears only on the second attempt?

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.

Add Trailing Swipe Actions Configuration with white background color and dark gray icon

I am trying to add Trailing Swipe Actions for UITableView using UIContextualAction but the icon is not visible because the icon becomes white and the background is also white and when I change the background color it is visible. Please help me set the icon as such with its color. I tried searching for tint color property but there is no such property. Below are the codes I have tried.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .normal, title: nil, handler: { (action,view,completionHandler ) in
// Delete the row after API
})
//Solution 1
delete.image = UIImage(named: "deleteicon")
// Solution 2
delete.image = UIGraphicsImageRenderer(size:CGSize(width: 30, height: 30)).image { _ in
UIImage(named:"deleteicon")?.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30))
}
// Solution 3
edit.backgroundColor = UIColor(patternImage: UIGraphicsImageRenderer(size:CGSize(width: 30, height: 30)).image { _ in UIImage(named:"favouriteinactiveicon")?.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30))})
let confrigation = UISwipeActionsConfiguration(actions: [delete])
return confrigation
}

Working with images in tableView:editActionsForRowAt:

Here is some trouble I am having working with a UITableView, more precisely using an image in tableView:editActionsForRowAt:
Below follows my relevant code. It may be useful to say that the images I am using here are all square 70x70.
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
var patternImg:UIImage?
let upBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("upBtn button tapped")
}
patternImg = self.swipeCellImage(named: "UpIcn", side: 46.0)
upBtn.backgroundColor = UIColor(patternImage: patternImg!)
let downBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("downBtn button tapped")
}
patternImg = swipeCellImage(named: "DownIcn", side: 46.0)
downBtn.backgroundColor = UIColor(patternImage: patternImg!)
let rmvBtn = UITableViewRowAction(style: .destructive, title: "") {
action, index in
print("rmvBtn button tapped")
}
patternImg = swipeCellImage(named: "TrashIcn", side: 46.0)
rmvBtn.backgroundColor = UIColor(patternImage: patternImg!)
return [downBtn,upBtn,rmvBtn]
}
func swipeCellImage(named name: String, side: CGFloat) -> UIImage? {
let theImage = UIImage(named: name)
UIGraphicsBeginImageContextWithOptions(CGSize(width: side*2, height: side), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.clear.cgColor)
theImage?.draw(in: CGRect(x: 0, y: 0, width: side, height: side))
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
Things work, but unfortunately only up to a certain point.
Looking at this screen shot shows that there is an unwanted repetion of the images. Clearly I want each button(Icon) to only appear one time each.
Can anyone see how I need to fix my code to avoid this repetion?
To avoid that, you have to increase width size asper your requirement.
UIGraphicsBeginImageContextWithOptions(CGSize(width: self.view.frame.width, height: commonHei), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(textColor.cgColor) // CHECK BY CHANGE BGCOLOR
context!.fill(CGRect(x: 0, y: 0, width: (self.view.frame.width) / 3, height: commonHei)) // INCREASE WIDTH SIZE
var img: UIImage = UIImage(named: "pause")!
img.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30)) // Change position as per ur requirement.
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
If you having more doubt, then check on the 3D view.

Aligning multiple runtime generated UILabels in a UITableView

I have a UITableView that needs to support content by listing style something like
But the tricky part is that the amount of "Label" will vary with each cell, some may have only 4 but also up to 12. Also the "Value" can be either a single word or short phrase up to two lines(like in the image above). So I decided to use UIStackView to help me pack and size my UILabels used to display this. I am currently stuck at a problem when the "Label" varies in length like:
I need the leading end of the "Values" to be aligned like the first image even though the "Label" vary in length. Is there a behaviour of UIStackView that allows so? Or is there another approach that can allow me to obtain the results I need?
Note: Each "Label" and "Value" is in one UIStackView, I did it to align them.
I tried using String Formatting too, but "Values" with more than one line will wrap under the label instead of wrapping by itself like I manage to do in the images.
I tried placing all "Labels" in one UIStackView and all "Values" in another, I could no get them to align like they do in the images once the "Value" is more than one line.
Or if it might be a mistake I made somewhere, this is how I created the UIStackViews:
var higherCount = 0
if labels.count<values.count {
higherCount = values.count
} else {
higherCount = labels.count
}
mainStackView = UIStackView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
for i in 0..<higherCount {
var height:CGFloat = 20
if (values[i] as NSString).size(attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: UIFont.systemFontSize)]).width > frame.width {
height = 42
}
let stackViewToAdd = UIStackView(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))
let label = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))
if labels.count<higherCount {
label.text = ""
} else {
label.text = labels[i]
}
label.setContentCompressionResistancePriority(1000, for: .horizontal)
label.setContentHuggingPriority(999, for: .horizontal)
stackViewToAdd.addArrangedSubview(label)
let value = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))
if values.count<higherCount {
value.text = ""
} else {
value.text = values[i]
}
value.lineBreakMode = .byWordWrapping
value.numberOfLines = 2
stackViewToAdd.addArrangedSubview(value)
mainStackView?.addArrangedSubview(stackViewToAdd)
}
mainStackView?.alignment = .fill
mainStackView?.axis = .vertical
mainStackView?.distribution = .fill
I you created your cell programmatically, then you can resize the cell programmatically depends on the size of UILabel Content.
In my case Label font is UIFont.systemFont(ofSize: 15), minimum TableViewCell height is 50, arrLabel1 and arrLabel2 will be the content of Labels.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrLable1.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "\(indexPath.row)")
let lable1 = UILabel(frame: CGRect(x: 10, y: 10, width: view.frame.size.width - 20, height: 0))
lable1.numberOfLines = 0
lable1.font = UIFont.systemFont(ofSize: 15)
lable1.text = arrLable1[indexPath.row]
lable1.sizeToFit()
cell.addSubview(lable1)
let lable2 = UILabel(frame: CGRect(x: 10, y: lable1.frame.origin.y + lable1.frame.size.height + 10 , width: view.frame.size.width - 20, height: 0))
lable2.numberOfLines = 0
lable2.font = UIFont.systemFont(ofSize: 15)
lable2.text = arrLable2[indexPath.row]
lable2.sizeToFit()
cell.addSubview(lable2)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
-> CGFloat {
let boundingRect1 = arrLable1[indexPath.row].boundingRect(with: CGSize(width: view.frame.size.width - 40 , height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin,attributes:[ NSFontAttributeName : UIFont.systemFont(ofSize: 15)] ,context: nil)
let boundingRect2 = arrLable2[indexPath.row].boundingRect(with: CGSize(width: view.frame.size.width - 40 , height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin,attributes:[ NSFontAttributeName : UIFont.systemFont(ofSize: 15)] ,context: nil)
guard boundingRect1.height + boundingRect2.height + 30 > 50 else {
return 50
}
return boundingRect1.height + boundingRect2.height + 30
}
Set Label numberOfLines to 0

Set the position of Image View

I want to change the position of image view that is based on different devices.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier0, forIndexPath: indexPath) as! CelebrityHeaderCell
let CelebrityTopObj = self.mCelebrityTop[indexPath.item]
if CelebrityTopObj.video_pic != nil
{
SDWebImageManager.sharedManager().downloadImageWithURL(NSURL(string: CelebrityTopObj.video_pic), options: .LowPriority, progress: { (min:Int, max:Int) -> Void in
}) { (vimage:UIImage!, error:NSError!, cacheType:SDImageCacheType, finished:Bool, url:NSURL!) -> Void in
if vimage != nil && finished
{
if getScreenHeight()<=568 {
cell.VideoImg.image = imageResize(vimage, sizeChange: CGSize(width: self.screenWidth/2.5, height: self.screenWidth/3))
cell.PrevIcon.image = imageResize(UIImage(named: "icon_prev")!, sizeChange: CGSize(width: 25, height: 25))
cell.NextIcon.image = imageResize(UIImage(named: "icon_next")!, sizeChange: CGSize(width: 25, height: 25))
cell.TopName.font = cell.TopName.font.fontWithSize(15)
cell.PrevIcon.frame.origin.x = 10
cell.PrevIcon.frame.origin.y = 30
cell.PrevIcon.frame = CGRect(x: 10, y: 30, width: 25, height: 25)
} else {
cell.VideoImg.image = imageResize(vimage, sizeChange: CGSize(width: self.screenWidth/2, height: 100))
cell.PrevIcon.image = imageResize(UIImage(named: "icon_prev")!, sizeChange: CGSize(width: 55, height: 55))
cell.NextIcon.image = imageResize(UIImage(named: "icon_next")!, sizeChange: CGSize(width: 55, height: 55))
}
}
}
}
return cell
}
I want to set the position of PrevIcon by using frame.origin.x and frame.origin.y but it doesn't work. How do I do to set the position?
http://gfamily.cwgv.com.tw/public/images/iphone5.png
To set the new position try something like that:
cell.PrevIcon.frame = CGRect(x: 10, y: 50, width: cell.PrevIcon.frame.bounds.width, height: cell.PrevIcon.frame.bounds.width)
To change a parameter of the "frame" you have to set it with CGRect(). You cant edit just a single value.
Also have a look at size classes in storyboard. Size classes enables you to have different layout constraints for different device types and orientations. Here is a link to a quick tutorial.

Resources