I have customize Swipe delete button till swift3 [ios 9].
I have tried this code for ios 9 in swift3
func setSwipeDeletebutton(cell : UITableViewCell, BackView : UIView) {
//Customize Delete button
for subview in cell.subviews {
subview.backgroundColor = UIColor.clear
for sub in subview.subviews {
if String(describing: sub).range(of: "UITableViewCellActionButton") != nil {
sub.backgroundColor = UIColor.white
var newframe = sub.frame
newframe.size.height = BackView.frame.size.height
sub.frame = newframe
sub.SetUpView()
for view in sub.subviews {
if String(describing: view).range(of: "UIButtonLabel") != nil {
if let label = view as? UILabel {
label.textColor = SharedInstance.hexStringToUIColor(hex: Color.TodayActivityColor.Type_LEAVE)
}
}
}
}
}
}
}
Now, as a change in ios 11, I can not customize swipe delete button using this code.
I have tried to set image as swipe delete button, but it's not set properly because I also want shadow to that delete button.
Can anyone suggest that how can I customize the swipe delete button using swift4?
You are accessing subviews by using:
String(describing: sub).range(of: "UITableViewCellActionButton")
And
String(describing: view).range(of: "UIButtonLabel")
This is not advisable by apple.
However, to debug this issue open your view hierarchy while the table view row action / drawer is open.
You should see the view you wish to alter, in the hierarchy.
Then change your Strings as required:
String(describing: sub).range(of: "UISwipeActionPullView")
UISwipeActionPullView may not be what you need but the concept is the same.
Related
I am using 'UIKeyboardTypeDefault' keyboard type for my UITextfields.
The done button for the keyboard appears in lowercase as 'done'.
Is there anyway to make this a capitalised as 'Done' ?
You can try using the POD - > IQKeyboardManagerSwift
LINK -->
pod 'IQKeyboardManagerSwift', :git =>
'https://github.com/hackiftekhar/IQKeyboardManager.git'
This is very useful pod for handling stuff for keyboard
We cannot set a custom text for the return key, we can only select from the available options (refer attached screenshot).
In order to set a custom text, for e.g capitalised 'done' button, you will have to create a custom keyboard. attached screenshot
I've been struggling with the same question for tvOS for the last day or so, and figured out the solution is to get the 'to' viewController of the presented keyboard, iterate over its view hierarchy to find the button, then grab its text, capitalise it, and set it back.
Ended up with the following function:
fileprivate func CapitaliseReturnKey() {
let scene = UIApplication.shared.connectedScenes.first
guard
let windowSceneDelegate = scene?.delegate as? UIWindowSceneDelegate,
let window = windowSceneDelegate.window,
// This gets the presented keyboard view, which is the 'to' view for the presentation transition.
let keyboardView = window?.rootViewController?.transitionCoordinator?.view(forKey: .to)
else {
return
}
capitaliseReturnKey(in: keyboardView)
/// Traverses the view heirarchy of the passed-in view looking for a UIButton and capitalises its title.
/// - Parameter view: the view to iterate over
func capitaliseReturnKey(in view: UIView) {
for subView in view.subviews {
if subView.subviews.count > 0 {
capitaliseReturnKey(in: subView)
}
if subView.isKind(of: UIButton.self) {
if let buttonView = subView as? UIButton {
buttonView.setTitle(buttonView.currentTitle?.localizedCapitalized, for: .normal)
}
}
}
}
I want to add a banner to the navigation bar, but by increasing the height of it. I want to copy the design and behaviour of an artist page in the Apple Music app:
It behaves just like a normal Large Title would, except for that it has been moved down, it has a sticky UIImageView behind it and it returns its background when the user scrolls down far enough. You can fire up Apple Music, search for an artist and go to their page to try it out yourself.
I've tried a bunch of things like setting the frame on the UINavigationBarLargeTitleView, and the code from this answer: https://stackoverflow.com/a/49326161/5544222
I already got a hold of the UINavigationBarLargeTitleView and its UILabel using the following code:
func setLargeTitleHeight() {
if let largeTitleView = self.getLargeTitleView() {
if let largeTitleLabel = self.getLargeTitleLabel(largeTitleView: largeTitleView) {
// Set largeTitleView height.
}
}
}
func getLargeTitleView() -> UIView? {
for subview in self.navigationBar.subviews {
if NSStringFromClass(subview.classForCoder).contains("UINavigationBarLargeTitleView") {
return subview
}
}
return nil
}
func getLargeTitleLabel(largeTitleView: UIView) -> UILabel? {
for subview in largeTitleView.subviews {
if subview.isMember(of: UILabel.self) {
return (subview as! UILabel)
}
}
return nil
}
Initially put a view with image and label and play button. Then clear the navigation bar it will show the image below it by
self.navigationController!.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController!.navigationBar.shadowImage = UIImage()
self.navigationController!.navigationBar.isTranslucent = true
later when you scroll up you have to handle it manually and again show the
navigation with title.
I'm using UIAlertController for some actions.
But I'm not a big fan of the Blurry View Effect in the actions group view (see screenshot below).
I'm trying to remove this blurry effect. I made some research online, and I couldn't find any API in UIAlertController that allows to remove this blurry effect. Also, according to their apple doc here :
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
I see that Instagram also removes this blurry view effect :
The only way I could find to remove it is to update the view hierarchy myself via an extension of UIAlertController.
extension UIAlertController {
#discardableResult private func findAndRemoveBlurEffect(currentView: UIView) -> Bool {
for childView in currentView.subviews {
if childView is UIVisualEffectView {
childView.removeFromSuperview()
return true
} else if String(describing: type(of: childView.self)) == "_UIInterfaceActionGroupHeaderScrollView" {
// One background view is broken, we need to make sure it's white.
if let brokenBackgroundView = childView.superview {
// Set broken brackground view to a darker white
brokenBackgroundView.backgroundColor = UIColor.colorRGB(red: 235, green: 235, blue: 235, alpha: 1)
}
}
findAndRemoveBlurEffect(currentView: childView)
}
return false
}
}
let actionSheetController = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
actionSheetController.view.tintColor = .lightBlue
actionSheetController.removeBlurryView()
This worked fine, it removed my blurry view effect:
What I'm wondering... Is my solution the only way to accomplish that? Or there is something that I'm missing about the Alert Controller appearance?
Maybe there is a cleaner way to accomplish exactly that result? Any other ideas?
It is easier to subclass UIAlertController.
The idea is to traverse through view hierarchy each time viewDidLayoutSubviews gets called, remove effect for UIVisualEffectView's and update their backgroundColor:
class AlertController: UIAlertController {
/// Buttons background color.
var buttonBackgroundColor: UIColor = .darkGray {
didSet {
// Invalidate current colors on change.
view.setNeedsLayout()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Traverse view hierarchy.
view.allViews.forEach {
// If there was any non-clear background color, update to custom background.
if let color = $0.backgroundColor, color != .clear {
$0.backgroundColor = buttonBackgroundColor
}
// If view is UIVisualEffectView, remove it's effect and customise color.
if let visualEffectView = $0 as? UIVisualEffectView {
visualEffectView.effect = nil
visualEffectView.backgroundColor = buttonBackgroundColor
}
}
// Update background color of popoverPresentationController (for iPads).
popoverPresentationController?.backgroundColor = buttonBackgroundColor
}
}
extension UIView {
/// All child subviews in view hierarchy plus self.
fileprivate var allViews: [UIView] {
var views = [self]
subviews.forEach {
views.append(contentsOf: $0.allViews)
}
return views
}
}
Usage:
Create alert controller.
Set buttons background color:
alertController.buttonBackgroundColor = .darkGray
Customise and present controller.
Result:
Answer by Vadim works really well.
What I missed in it (testing on iOS 14.5) is lack of separators and invisible title and message values.
So I added setting correct textColor for labels and skipping separator visual effect views in order to get correct appearance. Also remember to override traitCollectionDidChange method if your app supports dark mode to update controls backgroundColor accordingly
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for subview in view.allViews {
if let label = subview as? UILabel, label.textColor == .white {
label.textColor = .secondaryLabel
}
if let color = subview.backgroundColor, color != .clear {
subview.backgroundColor = buttonBackgroundColor
}
if let visualEffectView = subview as? UIVisualEffectView,
String(describing: subview).contains("Separator") == false {
visualEffectView.effect = nil
visualEffectView.contentView.backgroundColor = buttonBackgroundColor
}
}
popoverPresentationController?.backgroundColor = buttonBackgroundColor
}
I have been search a while for this issue , I want my search bar display like BBC News App
I try all related method
for view in searchBar.subviews {
if view.isKindOfClass(NSClassFromString("UISearchBarBackground")!) {
view.removeFromSuperview()
break;
}
}
self.searchBar.tintColor = UIColor.clearColor()
self.searchBar.backgroundColor = UIColor.clearColor()
self.searchBar.translucent = true
here is my output
Am I miss something ??? Please Help me , thx !
Swift 3
To remove the background altogether, set backgroundImage to an empty image:
searchBar.backgroundImage = UIImage()
To set a custom background color, use barTintcolor property:
searchBar.barTintColor = .green
Thx all , I solve the question by setting the background image to 'nil' , which is a nonexistent image in my app
my final output
==================== Update Final Solution ====================
After read more documents . Finally I found a better solution ,
for subView in searchBar.subviews {
for view in subView.subviews {
if view.isKindOfClass(NSClassFromString("UINavigationButton")!) {
let cancelButton = view as! UIButton
cancelButton.setTitle("取消", forState: UIControlState.Normal)
cancelButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
}
if view.isKindOfClass(NSClassFromString("UISearchBarBackground")!) {
let imageView = view as! UIImageView
imageView.removeFromSuperview()
}
}
}
==================== Update Swift4 ====================
for subView in searchBar.subviews {
for view in subView.subviews {
if view.isKind(of: NSClassFromString("UINavigationButton")!) {
let cancelButton = view as! UIButton
cancelButton.setTitleColor(.white, for: .normal)
cancelButton.setTitle("取消", for: .normal)
}
if view.isKind(of: NSClassFromString("UISearchBarBackground")!) {
let imageView = view as! UIImageView
imageView.removeFromSuperview()
}
}
}
Alternate version as an extension
extension UISearchBar {
func removeBackgroundImageView(){
if let view:UIView = self.subviews.first {
for curr in view.subviews {
guard let searchBarBackgroundClass = NSClassFromString("UISearchBarBackground") else {
return
}
if curr.isKind(of:searchBarBackgroundClass){
if let imageView = curr as? UIImageView{
imageView.removeFromSuperview()
break
}
}
}
}
}
}
In my case it helped:
searchView.backgroundImage = UIImage()
searchView.searchTextField.backgroundColor = .white
The current answers will cause runtime errors if run within iOS 13:
Terminating app due to uncaught exception 'NSGenericException', reason:
'Missing or detached view for search bar layout. The application must not remove
<UISearchBarBackground: 0x102d05050; frame = (0 0; 414 56); alpha = 0; hidden = YES;
userInteractionEnabled = NO; layer = <CALayer: 0x280287420>> from the hierarchy.'
If the code must be run by devices between iOS 9 and iOS 13, then the below is a possible solution.
First, create an extension that allows for the recursive finding of a subview based on a class name:
extension UIView {
/// Find the first subview of the specified class.
/// - Parameter className: The class name to search for.
/// - Parameter usingRecursion: True if the search should continue through the subview tree until a match is found; false otherwise
/// - Returns: The first child UIView of the specified class
func findSubview(withClassName className: String, usingRecursion: Bool) -> UIView? {
// If we can convert the class name until a class, we look for a match in the subviews of our current view
if let reflectedClass = NSClassFromString(className) {
for subview in self.subviews {
if subview.isKind(of: reflectedClass) {
return subview
}
}
}
// If recursion was specified, we'll continue into all subviews until a view is found
if usingRecursion {
for subview in self.subviews {
if let tempView = subview.findSubview(withClassName: className, usingRecursion: usingRecursion) {
return tempView
}
}
}
// If we haven't returned yet, there was no match
return nil
}
}
Then, instead of removing the subview, make it fully transparent. The backgroundColorView view is the color that shows up directly underneath the text, but adjusting it is not a necessary part of the solution.
// On iOS 9, there is still an image behind the search bar. We want to remove it.
if let backgroundView = searchBar.findSubview(withClassName: "UISearchBarBackground", usingRecursion: true) {
backgroundView.alpha = 0
}
// The color on iOS 9 is white. This mimics the newer appearance of the post-iOS 9
// search controllers
if let backgroundColorView = searchBar.findSubview(withClassName: "_UISearchBarSearchFieldBackgroundView", usingRecursion: true) as? UIImageView {
backgroundColorView.backgroundColor = UIColor.lightGray
backgroundColorView.layer.cornerRadius = 8
backgroundColorView.alpha = 0.3
backgroundColorView.image = nil
}
How would I loop through all UIButtons in my view in Swift? I would want to set all the titles to "", but my for-loop in Swift is giving an error.
for btns in self.view as [UIButton] {
// set the title to ""
}
This code should work:
for view in self.view.subviews as [UIView] {
if let btn = view as? UIButton {
btn.setTitleForAllStates("")
}
}
You need to iterate through the subViews array.
Shortened and updated for Swift 3 & 4
for case let button as UIButton in self.view.subviews {
button.setTitleForAllStates("")
}
Looping over subview works, but it's sometimes a little ugly, and has other issues.
If you need to loop over some specific buttons, for example to add corner radius or change tint or background color, you can use an array of IBOutlets and then loop over that.
var buttons = [SkipBtn, AllowBtn]
for button in buttons as! [UIButton] {
button.layer.cornerRadius = 5
}
Swift 4:
let subviewButtons = self.view.subviews.filter({$0.isKind(of: UIButton.self)})
for button in subviewButtons {
//do something
}
To add some context for a common use case, suppose the buttons were in a scroll view and you wanted to highlight the tapped button and de-highlight the other buttons. In this situation, you would direct all buttons to one action method:
#objc private func buttonAction(_ button: UIButton) {
for case let b as UIButton in view.scrollView.subviews {
if b == button {
b.setTitleColor(UIColor.green, for: []) // highlight
} else {
b.setTitleColor(UIColor.black, for: []) // de-highlight
}
}
}
This code seems to be quite useful for iterating over any object within a view, just change UIButton for any other subview type such as UIView or UIImageView, etc.
let filteredSubviews = self.view.subviews.filter({
$0.isKindOfClass(UIButton)})
for view in filteredSubviews {
//Do something
}
Used some of the offered questions out there and created my own. I believe is the most efficient when you want to programmatically set up the title of various UIButtons(in my case I am building a quiz)
By randomising my array list and with just a for loop I printing the item at index to the button title
for view in self.viewForButtons.subviews{
if view.isKindOfClass(UIButton)
{
let button : UIButton = view as! UIButton
button.setTitle("item[i]", forState: .Normal)
}
}
If you have UIView's within self.view then you need to loop through the subviews while searching for UIButton. Using the accepted answer, I made this little function to do so:
Swift 4 + :
func findButton(`in` view: UIView){
for view in view.subviews as [UIView] {
if let button = view as? UIButton {
// Do something with 'button'
}else{
// Loop through subview looking for buttons
findButton(in: view)
}
}
}
Usage:
override func viewDidLoad() {
findButton(in: self.view)
}
Hope this helps!
Here's a short way in Swift if you know the subview only has buttons:
myView.subviews.map {
($0 as? UIButton)!.enabled = false
}